Commit 0db1ae2c authored by Léo-Paul Géneau's avatar Léo-Paul Géneau 👾

Add source code to use MavSDK and open62541

Add a wrapper for fixedwings drone control functions of MavSDK as well as code to use publish and subscribe functionnalities of OPC-UA # Please enter the commit message for your changes. Lines starting
parent 8305f340
CPPFLAGS=-g -Wall -Wno-array-bounds -Wno-format-truncation -std=c99 -Iinclude -O2 -fPIC
LDFLAGS+= -g
LIBS=-lstdc++ -lmavsdk -lmavsdk_action -lmavsdk_mavlink_passthrough -lmavsdk_telemetry -lopen62541
LIB_NAME := libqjswrapper.so
SRCS := mavsdk_wrapper.cpp pubsub_publish.c pubsub_subscribe.c qjs_wrapper.c
OBJS := mavsdk_wrapper.o pubsub_publish.o pubsub_subscribe.o qjs_wrapper.o
all: $(LIB_NAME)
%.o:
$(LIB_NAME): $(OBJS)
$(CC) $(LDFLAGS) -shared -o $@ $^ $(LIBS)
install: all
mkdir -p "$(DESTDIR)$(prefix)/lib"
install -m644 $(LIB_NAME) "$(DESTDIR)$(prefix)/lib"
clean:
$(RM) -f $(OBJS) $(LIB_NAME)
#ifndef __MAVSDK_H__
#define __MAVSDK_H__
#ifdef __cplusplus
extern "C" {
#endif
#include <stdint.h>
// Connexion management functions
int mavsdk_start(const char * url, const char * log_file, int timeout,
void (*publishCoordinates)(double, double, float));
int mavsdk_stop(void);
int mavsdk_reboot(void);
// Flight state management functions
int mavsdk_arm(void);
int mavsdk_doParachute(int param);
int mavsdk_loiter(double radius);
int mavsdk_land(void);
int mavsdk_takeOff(void);
int mavsdk_takeOffAndWait(void);
// Flight management functions
int mavsdk_doReposition(double la, double lo, double a, double y);
int mavsdk_loiterUnlimited(double radius, double la, double lo, double a);
int mavsdk_setAirspeed(double airspeed);
int mavsdk_setAltitude(double altitude);
int mavsdk_setTargetAltitude(double a);
int mavsdk_setTargetCoordinates(double la, double lo, double a, double y);
int mavsdk_setTargetCoordinatesXYZ(double x, double y, double z);
int mavsdk_setTargetLatLong(double la, double lo);
// Information functions
double mavsdk_getAltitude(void);
double mavsdk_getInitialAltitude(void);
double mavsdk_getInitialLatitude(void);
double mavsdk_getInitialLongitude(void);
double mavsdk_getLatitude(void);
double mavsdk_getLongitude(void);
double mavsdk_getPitch(void);
double mavsdk_getRoll(void);
double mavsdk_getTakeOffAltitude(void);
double mavsdk_getYaw(void);
int mavsdk_healthAllOk(void);
int mavsdk_landed(void);
#ifdef __cplusplus
}
#endif
#endif /* __MAVSDK_H__ */
#ifndef __PUBSUB_COMMON_H__
#define __PUBSUB_COMMON_H__
#include <open62541/server.h>
#define countof(x) (sizeof(x) / sizeof((x)[0]))
#endif /* __PUBSUB_COMMON_H__ */
\ No newline at end of file
#ifndef __PUBSUB_PUBLISH_H__
#define __PUBSUB_PUBLISH_H__
#include "pubsub_common.h"
void writeFloat(char *name, float value);
void writeDouble(char *name, double value);
int publish(UA_String *transportProfile,
UA_NetworkAddressUrlDataType *networkAddressUrl,
UA_Boolean *running);
#endif /* __PUBSUB_PUBLISH_H__ */
#ifndef __PUBSUB_SUBSCRIBE_H__
#define __PUBSUB_SUBSCRIBE_H__
#include "pubsub_common.h"
int subscribe(UA_String *transportProfile,
UA_NetworkAddressUrlDataType *networkAddressUrl,
UA_Boolean *running);
#endif /* __PUBSUB_SUBSCRIBE_H__ */
#include <chrono>
#include <cmath>
#include <cstdint>
#include <mavsdk/mavsdk.h>
#include <mavsdk/plugins/action/action.h>
#include <mavsdk/plugins/mavlink_passthrough/mavlink_passthrough.h>
#include <mavsdk/plugins/telemetry/telemetry.h>
#include <iostream>
#include <fstream>
#include <functional>
#include <future>
#include <memory>
#include <thread>
#include "mavsdk_wrapper.h"
using namespace mavsdk;
using std::chrono::seconds;
using std::this_thread::sleep_for;
#define ERROR_CONSOLE_TEXT "\033[31m" // Turn text on console red
#define TELEMETRY_CONSOLE_TEXT "\033[34m" // Turn text on console blue
#define NORMAL_CONSOLE_TEXT "\033[0m" // Restore normal console colour
static std::ofstream log_file_fd;
static Mavsdk _mavsdk;
static Telemetry * telemetry;
static Action * action;
static MavlinkPassthrough * mavlink_passthrough;
static std::shared_ptr<System> msystem;
static auto prom = std::promise<std::shared_ptr<System>>{};
static std::future<std::shared_ptr<System>> fut;
static double drone_la;
static double drone_lo;
static double drone_roll;
static double drone_pitch;
static double drone_yaw;
static float drone_a;
static float drone_at;
static Telemetry::FlightMode flight_mode;
static double initial_drone_la;
static double initial_drone_lo;
static double initial_drone_la_rad;
static double initial_drone_lo_rad;
static float initial_drone_a;
static double xy_ratio;
static bool initial_coords_set = false;
static const double EARTH_RADIUS = 6371000.F;
static int mavsdk_started = 0;
static void (*publish_fn)(double, double, float);
// Logs functions
static void log(std::string message) {
log_file_fd << message << std::endl;
}
static void log_error(std::string message) {
log(ERROR_CONSOLE_TEXT + message + NORMAL_CONSOLE_TEXT);
}
template <class Enumeration>
static void log_error_from_result(std::string message, Enumeration result) {
std::ostringstream oss;
oss << message << ": " << result;
log_error(oss.str());
}
static void log_telemetry(std::string message) {
// set to blue set to default color again
log(TELEMETRY_CONSOLE_TEXT + message + NORMAL_CONSOLE_TEXT);
}
// Action functions
static int doAction(Action::Result (Action::*toDo)(void) const, std::string failure_message) {
if(!mavsdk_started) {
log_error("Mavsdk not started");
return -1;
}
const Action::Result result = (action->*toDo)();
if(result != Action::Result::Success) {
log_error_from_result(failure_message, result);
return -1;
}
return 0;
}
static int doMavlinkCommand(MavlinkPassthrough::CommandLong command, std::string failure_message) {
const MavlinkPassthrough::Result result = mavlink_passthrough->send_command_long(command);
if(result != MavlinkPassthrough::Result::Success) {
log_error_from_result(failure_message, result);
return -1;
}
return 0;
}
// Connexion management functions
int mavsdk_start(const char * url, const char * log_file, int timeout,
void (*publishCoordinates)(double, double, float)) {
std::string connection_url(url);
ConnectionResult connection_result;
log_file_fd.open(log_file);
connection_result = _mavsdk.add_any_connection(connection_url);
if (connection_result != ConnectionResult::Success) {
log_error_from_result("Connection failed", connection_result);
return -1;
}
log("Waiting to discover msystem...");
fut = prom.get_future();
_mavsdk.subscribe_on_new_system([]() {
auto msystem_tmp = _mavsdk.systems().back();
if (msystem_tmp->has_autopilot()) {
log("Discovered autopilot");
// Unsubscribe again as we only want to find one system.
_mavsdk.subscribe_on_new_system(nullptr);
prom.set_value(msystem_tmp);
}
});
if (fut.wait_for(seconds(timeout)) == std::future_status::timeout) {
log_error("No autopilot found, exiting.");
return -1;
}
msystem = fut.get();
telemetry = new Telemetry(msystem);
action = new Action(msystem);
mavlink_passthrough = new MavlinkPassthrough(msystem);
log("Subscribing to flight mode...");
// Subscribe to receive updates on flight mode. You can find out whether FollowMe is active.
telemetry->subscribe_flight_mode([](Telemetry::FlightMode _flight_mode) {
if(flight_mode != _flight_mode) {
flight_mode = _flight_mode;
}
});
/*log("Subscribing to Euler angle...");
telemetry->subscribe_attitude_euler([](Telemetry::EulerAngle euler_angle) {
drone_roll = euler_angle.roll_deg;
drone_pitch = euler_angle.pitch_deg;
drone_yaw = euler_angle.yaw_deg;
});*/
log("Subscribing to position...");
// Set up callback to monitor altitude while the vehicle is in flight
publish_fn = publishCoordinates;
telemetry->subscribe_position([](Telemetry::Position position) {
drone_la = position.latitude_deg;
drone_lo = position.longitude_deg;
drone_a = position.absolute_altitude_m;
drone_at = position.relative_altitude_m;
publish_fn(drone_la, drone_lo, drone_a);
if(!initial_coords_set && drone_la != 0) {
initial_drone_la = drone_la;
initial_drone_lo = drone_lo;
initial_drone_la_rad = (M_PI * drone_la) / 180.F;
initial_drone_lo_rad = (M_PI * drone_lo) / 180.F;
initial_drone_a = drone_a;
xy_ratio = std::cos(initial_drone_la_rad);
initial_coords_set = true;
}
std::ostringstream oss;
oss << drone_a << " m " << drone_at << " m "
<< drone_la << " " << drone_lo << " ";
log_telemetry(oss.str());
});
log("MAVSDK started...");
mavsdk_started = 1;
return 0;
}
int mavsdk_stop() {
if(!mavsdk_started)
return -1;
if(!mavsdk_landed()) {
log_error("You must land first !");
return -1;
}
if(doAction(&Action::shutdown, "Shutdown failed")) {
return -1;
}
// Delete pointers
delete action;
delete mavlink_passthrough;
delete telemetry;
log_file_fd.close();
return 0;
}
int mavsdk_reboot() {
return doAction(&Action::reboot, "Rebooting failed");
}
// Flight state management functions
int mavsdk_arm(void) {
if(!mavsdk_started)
return -1;
while(!telemetry->health().is_home_position_ok) {
log("Waiting for home position to be set");
sleep_for(seconds(1));
}
log("Arming...");
return doAction(&Action::arm, "Arming failed");
}
int mavsdk_doParachute(int param) {
if(!mavsdk_started)
return -1;
MavlinkPassthrough::CommandLong command;
command.command = MAV_CMD_DO_PARACHUTE;
command.param1 = param; //see https://mavlink.io/en/messages/common.html#PARACHUTE_ACTION
command.target_sysid = mavlink_passthrough->get_target_sysid();
command.target_compid = mavlink_passthrough->get_target_compid();
return doMavlinkCommand(command, "Parachute failed");
}
int mavsdk_land(void) {
log("Landing...");
if(doAction(&Action::terminate, "Land failed")) {
return -1;
}
// Check if vehicle is still in air
while(telemetry->in_air()) {
log("Vehicle is landing...");
sleep_for(seconds(1));
}
log("Landed!");
while(telemetry->armed()) {
sleep_for(seconds(1));
}
log("Finished...");
return 0;
}
int mavsdk_loiter(double radius) {
if(!mavsdk_started)
return -1;
if(flight_mode == Telemetry::FlightMode::Hold) {
std::cout << "Flight mode is " << flight_mode << std::endl;
return 0;
}
return loiterUnlimited(radius, drone_la, drone_lo, drone_a);
}
int mavsdk_takeOff(void) {
if(doAction(&Action::takeoff, "Takeoff failed")) {
return -1;
}
while(flight_mode != Telemetry::FlightMode::Takeoff) {
sleep_for(seconds(1));
}
log("Taking off...");
return 0;
}
int mavsdk_takeOffAndWait(void) {
if(mavsdk_takeOff() < 0) {
return -1;
}
while(flight_mode == Telemetry::FlightMode::Takeoff) {
sleep_for(seconds(1));
}
return 0;
}
// Flight management functions
int mavsdk_doReposition(double la, double lo, double a, double y) {
if(!mavsdk_started)
return -1;
MavlinkPassthrough::CommandLong command;
command.command = MAV_CMD_DO_REPOSITION;
command.param1 = -1; // Ground speed, -1 for default
command.param2 = 1; // Bitmask of option flags (https://mavlink.io/en/messages/common.html#MAV_DO_REPOSITION_FLAGS)
command.param4 = y; // loiter direction, 0: clockwise 1: counter clockwise
command.param5 = la;
command.param6 = lo;
command.param7 = a;
command.target_sysid = mavlink_passthrough->get_target_sysid();
command.target_compid = mavlink_passthrough->get_target_compid();
return doMavlinkCommand(command, "Reposition failed");
}
int loiterUnlimited(double radius, double la, double lo, double a) {
if(!mavsdk_started)
return -1;
MavlinkPassthrough::CommandLong command;
command.command = MAV_CMD_NAV_LOITER_UNLIM;
command.param2 = radius; // Loiter radius around waypoint. If positive loiter clockwise, else counter-clockwise
command.param5 = la;
command.param6 = lo;
command.param7 = a;
command.target_sysid = mavlink_passthrough->get_target_sysid();
command.target_compid = mavlink_passthrough->get_target_compid();
return doMavlinkCommand(command, "Loiter failed");
}
int mavsdk_setAirspeed(double airspeed) {
if(!mavsdk_started)
return -1;
MavlinkPassthrough::CommandLong command;
command.command = MAV_CMD_DO_OVERRIDE;
command.param1 = 1 | 2 | 4 | 8;
command.param2 = 2 | 4 | 8;
command.param3 = (float) airspeed;
command.target_sysid = mavlink_passthrough->get_target_sysid();
command.target_compid = mavlink_passthrough->get_target_compid();
return doMavlinkCommand(command, "Setting airspeed failed");
}
int mavsdk_setAltitude(double altitude) {
if(!mavsdk_started)
return -1;
MavlinkPassthrough::CommandLong command;
command.command = MAV_CMD_DO_OVERRIDE;
command.param1 = 1 | 2 | 4 | 8;
command.param2 = 1 | 2 | 8;
command.param3 = (float) altitude;
command.target_sysid = mavlink_passthrough->get_target_sysid();
command.target_compid = mavlink_passthrough->get_target_compid();
return doMavlinkCommand(command, "Setting altitude failed");
}
int mavsdk_setTargetAltitude(double a) {
if(!mavsdk_started)
return -1;
log("Going to altitude " + std::to_string(a) + "m");
const Action::Result result = action->goto_location(drone_la, drone_lo, (float) a, 0);
if (result != Action::Result::Success) {
log_error_from_result("Goto location failed", result);
return -1;
}
return 0;
}
int mavsdk_setTargetCoordinates(double la, double lo, double a, double y) {
if(!mavsdk_started)
return -1;
std::cout << "Going to location (" << la << " , " << lo << ") "
<< a << " m " << std::endl;
const Action::Result result = action->goto_location(la, lo, (float) a, (float) y);
if (result != Action::Result::Success) {
log_error_from_result("Goto location failed", result);
return -1;
}
return 0;
}
int mavsdk_setTargetCoordinatesXYZ(double x, double y, double z) {
double la, lo;
la = ((initial_drone_la_rad + y / EARTH_RADIUS) * 180.F) / M_PI;
lo = ((initial_drone_lo_rad + x / (EARTH_RADIUS * xy_ratio)) * 180.F) / M_PI;
return mavsdk_setTargetCoordinates(la, lo, z, 0);
}
int mavsdk_setTargetLatLong(double la, double lo) {
return mavsdk_setTargetCoordinates(la, lo, drone_a, 0);
}
// Information functions
double mavsdk_getAltitude(void) {
return drone_a;
}
double mavsdk_getInitialAltitude(void) {
return initial_drone_a;
}
double mavsdk_getInitialLatitude(void) {
return initial_drone_la;
}
double mavsdk_getInitialLongitude(void) {
return initial_drone_lo;
}
double mavsdk_getLatitude(void) {
return drone_la;
}
double mavsdk_getLongitude(void) {
return drone_lo;
}
double mavsdk_getPitch(void) {
return drone_pitch;
}
double mavsdk_getRoll(void) {
return drone_roll;
}
double mavsdk_getTakeOffAltitude(void) {
const std::pair<Action::Result, float> response = action->get_takeoff_altitude();
if(response.first != Action::Result::Success) {
log_error_from_result("Get takeoff altitude failed", response.first);
return -1;
}
return response.second;
}
double mavsdk_getYaw(void) {
return drone_yaw;
}
int mavsdk_healthAllOk(void) {
return telemetry->health_all_ok();
}
int mavsdk_landed(void) {
return !telemetry->in_air();
}
#include <open62541/plugin/log_stdout.h>
#include <open62541/plugin/pubsub_udp.h>
#include <open62541/server_config_default.h>
#include "pubsub_publish.h"
typedef struct PublishedVariable {
char *name;
char *description;
void * UA_RESTRICT pdefaultValue;
UA_DataType type;
} PublishedVariable;
static UA_Server *server;
UA_NodeId connectionIdent, publishedDataSetIdent, writerGroupIdent;
static void
addPubSubConnection(UA_Server *server, UA_String *transportProfile,
UA_NetworkAddressUrlDataType *networkAddressUrl){
/* Details about the connection configuration and handling are located
* in the pubsub connection tutorial */
UA_PubSubConnectionConfig connectionConfig;
memset(&connectionConfig, 0, sizeof(connectionConfig));
connectionConfig.name = UA_STRING("UADP Connection 1");
connectionConfig.transportProfileUri = *transportProfile;
connectionConfig.enabled = UA_TRUE;
UA_Variant_setScalar(&connectionConfig.address, networkAddressUrl,
&UA_TYPES[UA_TYPES_NETWORKADDRESSURLDATATYPE]);
/* Changed to static publisherId from random generation to identify
* the publisher on Subscriber side */
connectionConfig.publisherId.numeric = 2234;
UA_Server_addPubSubConnection(server, &connectionConfig, &connectionIdent);
}
/**
* **PublishedDataSet handling**
*
* The PublishedDataSet (PDS) and PubSubConnection are the toplevel entities and
* can exist alone. The PDS contains the collection of the published fields. All
* other PubSub elements are directly or indirectly linked with the PDS or
* connection. */
static void
addPublishedDataSet(UA_Server *server) {
/* The PublishedDataSetConfig contains all necessary public
* information for the creation of a new PublishedDataSet */
UA_PublishedDataSetConfig publishedDataSetConfig;
memset(&publishedDataSetConfig, 0, sizeof(UA_PublishedDataSetConfig));
publishedDataSetConfig.publishedDataSetType = UA_PUBSUB_DATASET_PUBLISHEDITEMS;
publishedDataSetConfig.name = UA_STRING("Demo PDS");
/* Create new PublishedDataSet based on the PublishedDataSetConfig. */
UA_Server_addPublishedDataSet(server, &publishedDataSetConfig, &publishedDataSetIdent);
}
static void
addVariable(UA_Server *server, PublishedVariable varDetails) {
UA_VariableAttributes attr = UA_VariableAttributes_default;
UA_Variant_setScalar(&attr.value, varDetails.pdefaultValue, &varDetails.type);
attr.description = UA_LOCALIZEDTEXT("en-US", varDetails.description);
attr.displayName = UA_LOCALIZEDTEXT("en-US", varDetails.description);
attr.dataType = varDetails.type.typeId;
attr.accessLevel = UA_ACCESSLEVELMASK_READ | UA_ACCESSLEVELMASK_WRITE;
UA_Server_addVariableNode(server, UA_NODEID_STRING(1, varDetails.name),
UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER),
UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES),
UA_QUALIFIEDNAME(1, varDetails.description),
UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE),
attr, NULL, NULL);
}
static void
writeVariable(char *name, void * UA_RESTRICT pvalue,
UA_DataType type)
{
UA_NodeId integerNodeId = UA_NODEID_STRING(1, name);
UA_Variant var;
UA_Variant_init(&var);
UA_Variant_setScalar(&var, pvalue, &type);
UA_Server_writeValue(server, integerNodeId, var);
}
void writeFloat(char *name, float externValue) {
float localValue = externValue;
writeVariable(name, &localValue, UA_TYPES[UA_TYPES_FLOAT]);
}
void writeDouble(char *name, double externValue) {
double localValue = externValue;
writeVariable(name, &localValue, UA_TYPES[UA_TYPES_DOUBLE]);
}
static void
addDataSetField(UA_Server *server, PublishedVariable varDetails) {
UA_NodeId dataSetFieldIdent;
UA_DataSetFieldConfig dataSetFieldConfig;
memset(&dataSetFieldConfig, 0, sizeof(UA_DataSetFieldConfig));
dataSetFieldConfig.dataSetFieldType = UA_PUBSUB_DATASETFIELD_VARIABLE;
dataSetFieldConfig.field.variable.fieldNameAlias = UA_STRING(varDetails.description);
dataSetFieldConfig.field.variable.promotedField = UA_FALSE;
dataSetFieldConfig.field.variable.publishParameters.publishedVariable =
UA_NODEID_STRING(1, varDetails.name);
dataSetFieldConfig.field.variable.publishParameters.attributeId = UA_ATTRIBUTEID_VALUE;
UA_Server_addDataSetField(server, publishedDataSetIdent,
&dataSetFieldConfig, &dataSetFieldIdent);
}
/**
* **WriterGroup handling**
*
* The WriterGroup (WG) is part of the connection and contains the primary
* configuration parameters for the message creation. */
static void
addWriterGroup(UA_Server *server) {
/* Now we create a new WriterGroupConfig and add the group to the existing
* PubSubConnection. */
UA_WriterGroupConfig writerGroupConfig;
memset(&writerGroupConfig, 0, sizeof(UA_WriterGroupConfig));
writerGroupConfig.name = UA_STRING("Demo WriterGroup");
writerGroupConfig.publishingInterval = 100;
writerGroupConfig.enabled = UA_FALSE;
writerGroupConfig.writerGroupId = 100;
writerGroupConfig.encodingMimeType = UA_PUBSUB_ENCODING_UADP;
writerGroupConfig.messageSettings.encoding = UA_EXTENSIONOBJECT_DECODED;
writerGroupConfig.messageSettings.content.decoded.type = &UA_TYPES[UA_TYPES_UADPWRITERGROUPMESSAGEDATATYPE];
/* The configuration flags for the messages are encapsulated inside the
* message- and transport settings extension objects. These extension
* objects are defined by the standard. e.g.
* UadpWriterGroupMessageDataType */
UA_UadpWriterGroupMessageDataType *writerGroupMessage = UA_UadpWriterGroupMessageDataType_new();
/* Change message settings of writerGroup to send PublisherId,
* WriterGroupId in GroupHeader and DataSetWriterId in PayloadHeader
* of NetworkMessage */
writerGroupMessage->networkMessageContentMask = (UA_UadpNetworkMessageContentMask)(UA_UADPNETWORKMESSAGECONTENTMASK_PUBLISHERID |
(UA_UadpNetworkMessageContentMask)UA_UADPNETWORKMESSAGECONTENTMASK_GROUPHEADER |
(UA_UadpNetworkMessageContentMask)UA_UADPNETWORKMESSAGECONTENTMASK_WRITERGROUPID |
(UA_UadpNetworkMessageContentMask)UA_UADPNETWORKMESSAGECONTENTMASK_PAYLOADHEADER);
writerGroupConfig.messageSettings.content.decoded.data = writerGroupMessage;
UA_Server_addWriterGroup(server, connectionIdent, &writerGroupConfig, &writerGroupIdent);
UA_Server_setWriterGroupOperational(server, writerGroupIdent);
UA_UadpWriterGroupMessageDataType_delete(writerGroupMessage);
}
/**
* **DataSetWriter handling**
*
* A DataSetWriter (DSW) is the glue between the WG and the PDS. The DSW is
* linked to exactly one PDS and contains additional information for the
* message generation. */
static void
addDataSetWriter(UA_Server *server) {
/* We need now a DataSetWriter within the WriterGroup. This means we must
* create a new DataSetWriterConfig and add call the addWriterGroup function. */
UA_NodeId dataSetWriterIdent;
UA_DataSetWriterConfig dataSetWriterConfig;
memset(&dataSetWriterConfig, 0, sizeof(UA_DataSetWriterConfig));
dataSetWriterConfig.name = UA_STRING("Demo DataSetWriter");
dataSetWriterConfig.dataSetWriterId = 62541;
dataSetWriterConfig.keyFrameCount = 10;
UA_Server_addDataSetWriter(server, writerGroupIdent, publishedDataSetIdent,
&dataSetWriterConfig, &dataSetWriterIdent);
}
int publish(UA_String *transportProfile,
UA_NetworkAddressUrlDataType *networkAddressUrl,
UA_Boolean *running) {
int i;
UA_Float defaultFloat = 0;
UA_Double defaultDouble = 0;
const PublishedVariable publishedVariableArray[] = {
{
.name = "lattitude",
.description = "Lattitude",
.pdefaultValue = &defaultDouble,
.type = UA_TYPES[UA_TYPES_DOUBLE],
},
{
.name = "longitude",
.description = "Longitude",
.pdefaultValue = &defaultDouble,
.type = UA_TYPES[UA_TYPES_DOUBLE],
},
{
.name = "altitude",
.description = "Altitude",
.pdefaultValue = &defaultFloat,
.type = UA_TYPES[UA_TYPES_FLOAT],
},
};
server = UA_Server_new();
UA_ServerConfig *config = UA_Server_getConfig(server);
UA_ServerConfig_setDefault(config);
UA_ServerConfig_addPubSubTransportLayer(config, UA_PubSubTransportLayerUDPMP());
addPubSubConnection(server, transportProfile, networkAddressUrl);
addPublishedDataSet(server);
for(i = 0; i < countof(publishedVariableArray); i++) {
addVariable(server, publishedVariableArray[i]);
addDataSetField(server, publishedVariableArray[i]);
}
addWriterGroup(server);
addDataSetWriter(server);
UA_StatusCode retval = UA_Server_run(server, running);
UA_Server_delete(server);
return retval == UA_STATUSCODE_GOOD ? EXIT_SUCCESS : EXIT_FAILURE;
}
#include <open62541/client_subscriptions.h>
#include <open62541/plugin/log_stdout.h>
#include <open62541/plugin/pubsub_udp.h>
#include <open62541/server_config_default.h>
#include <open62541/types_generated.h>
#include "ua_pubsub.h"
#include "pubsub_subscribe.h"
typedef struct SubscribedVariable {
UA_Byte builtInType;
UA_String name;
UA_NodeId dataType;
} SubscribedVariable;
UA_NodeId connectionIdentifier;
UA_NodeId readerGroupIdentifier;
UA_NodeId readerIdentifier;
UA_DataSetReaderConfig readerConfig;
static void fillDataSetMetaData(UA_DataSetMetaDataType *pMetaData,
SubscribedVariable *subscribedVariableArray,
int variableNb);
/* Add new connection to the server */
static UA_StatusCode
addPubSubConnection(UA_Server *server, UA_String *transportProfile,
UA_NetworkAddressUrlDataType *networkAddressUrl) {
if((server == NULL) || (transportProfile == NULL) ||
(networkAddressUrl == NULL)) {
return UA_STATUSCODE_BADINTERNALERROR;
}
UA_StatusCode retval = UA_STATUSCODE_GOOD;
/* Configuration creation for the connection */
UA_PubSubConnectionConfig connectionConfig;
memset (&connectionConfig, 0, sizeof(UA_PubSubConnectionConfig));
connectionConfig.name = UA_STRING("UDPMC Connection 1");
connectionConfig.transportProfileUri = *transportProfile;
connectionConfig.enabled = UA_TRUE;
UA_Variant_setScalar(&connectionConfig.address, networkAddressUrl,
&UA_TYPES[UA_TYPES_NETWORKADDRESSURLDATATYPE]);
connectionConfig.publisherId.numeric = UA_UInt32_random ();
retval |= UA_Server_addPubSubConnection (server, &connectionConfig, &connectionIdentifier);
if (retval != UA_STATUSCODE_GOOD) {
return retval;
}
return retval;
}
/**
* **ReaderGroup**
*
* ReaderGroup is used to group a list of DataSetReaders. All ReaderGroups are
* created within a PubSubConnection and automatically deleted if the connection
* is removed. All network message related filters are only available in the DataSetReader. */
/* Add ReaderGroup to the created connection */
static UA_StatusCode
addReaderGroup(UA_Server *server) {
if(server == NULL) {
return UA_STATUSCODE_BADINTERNALERROR;
}
UA_StatusCode retval = UA_STATUSCODE_GOOD;
UA_ReaderGroupConfig readerGroupConfig;
memset (&readerGroupConfig, 0, sizeof(UA_ReaderGroupConfig));
readerGroupConfig.name = UA_STRING("ReaderGroup1");
retval |= UA_Server_addReaderGroup(server, connectionIdentifier, &readerGroupConfig,
&readerGroupIdentifier);
UA_Server_setReaderGroupOperational(server, readerGroupIdentifier);
return retval;
}
/**
* **DataSetReader**
*
* DataSetReader can receive NetworkMessages with the DataSetMessage
* of interest sent by the Publisher. DataSetReader provides
* the configuration necessary to receive and process DataSetMessages
* on the Subscriber side. DataSetReader must be linked with a
* SubscribedDataSet and be contained within a ReaderGroup. */
/* Add DataSetReader to the ReaderGroup */
static UA_StatusCode
addDataSetReader(UA_Server *server,
SubscribedVariable *subscribedVariableArray,
int variableNb) {
if(server == NULL) {
return UA_STATUSCODE_BADINTERNALERROR;
}
UA_StatusCode retval = UA_STATUSCODE_GOOD;
memset (&readerConfig, 0, sizeof(UA_DataSetReaderConfig));
readerConfig.name = UA_STRING("DataSet Reader 1");
/* Parameters to filter which DataSetMessage has to be processed
* by the DataSetReader */
/* The following parameters are used to show that the data published by
* tutorial_pubsub_publish.c is being subscribed and is being updated in
* the information model */
UA_UInt16 publisherIdentifier = 2234;
readerConfig.publisherId.type = &UA_TYPES[UA_TYPES_UINT16];
readerConfig.publisherId.data = &publisherIdentifier;
readerConfig.writerGroupId = 100;
readerConfig.dataSetWriterId = 62541;
/* Setting up Meta data configuration in DataSetReader */
fillDataSetMetaData(&readerConfig.dataSetMetaData, subscribedVariableArray, variableNb);
retval |= UA_Server_addDataSetReader(server, readerGroupIdentifier, &readerConfig,
&readerIdentifier);
return retval;
}
static void
dataChangeNotificationCallback(UA_Server *server, UA_UInt32 monitoredItemId,
void *monitoredItemContext, const UA_NodeId *nodeId,
void *nodeContext, UA_UInt32 attributeId,
const UA_DataValue *var) {
if(UA_Variant_hasScalarType(&var->value, &UA_TYPES[UA_TYPES_DOUBLE])) {
UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND,
"Received Notification with value %f",
*(UA_Double*) var->value.data);
}
}
/**
* **SubscribedDataSet**
*
* Set SubscribedDataSet type to TargetVariables data type.
* Add subscribedvariables to the DataSetReader */
static UA_StatusCode
addSubscribedVariables (UA_Server *server, UA_NodeId dataSetReaderId) {
if(server == NULL)
return UA_STATUSCODE_BADINTERNALERROR;
UA_StatusCode retval = UA_STATUSCODE_GOOD;
UA_NodeId folderId;
UA_String folderName = readerConfig.dataSetMetaData.name;
UA_ObjectAttributes oAttr = UA_ObjectAttributes_default;
UA_QualifiedName folderBrowseName;
if(folderName.length > 0) {
oAttr.displayName.locale = UA_STRING ("en-US");
oAttr.displayName.text = folderName;
folderBrowseName.namespaceIndex = 1;
folderBrowseName.name = folderName;
}
else {
oAttr.displayName = UA_LOCALIZEDTEXT ("en-US", "Subscribed Variables");
folderBrowseName = UA_QUALIFIEDNAME (1, "Subscribed Variables");
}
UA_Server_addObjectNode (server, UA_NODEID_NULL,
UA_NODEID_NUMERIC (0, UA_NS0ID_OBJECTSFOLDER),
UA_NODEID_NUMERIC (0, UA_NS0ID_ORGANIZES),
folderBrowseName, UA_NODEID_NUMERIC (0,
UA_NS0ID_BASEOBJECTTYPE), oAttr, NULL, &folderId);
/**
* **TargetVariables**
*
* The SubscribedDataSet option TargetVariables defines a list of Variable mappings between
* received DataSet fields and target Variables in the Subscriber AddressSpace.
* The values subscribed from the Publisher are updated in the value field of these variables */
/* Create the TargetVariables with respect to DataSetMetaData fields */
UA_FieldTargetVariable *targetVars = (UA_FieldTargetVariable *)
UA_calloc(readerConfig.dataSetMetaData.fieldsSize, sizeof(UA_FieldTargetVariable));
for(size_t i = 0; i < readerConfig.dataSetMetaData.fieldsSize; i++) {
/* Variable to subscribe data */
UA_VariableAttributes vAttr = UA_VariableAttributes_default;
UA_LocalizedText_copy(&readerConfig.dataSetMetaData.fields[i].description,
&vAttr.description);
vAttr.displayName.locale = UA_STRING("en-US");
vAttr.displayName.text = readerConfig.dataSetMetaData.fields[i].name;
vAttr.dataType = readerConfig.dataSetMetaData.fields[i].dataType;
UA_NodeId newNode;
retval |= UA_Server_addVariableNode(server, UA_NODEID_NUMERIC(1, (UA_UInt32)i + 50000),
folderId,
UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT),
UA_QUALIFIEDNAME(1, (char *)readerConfig.dataSetMetaData.fields[i].name.data),
UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE),
vAttr, NULL, &newNode);
/*monitor variable*/
UA_MonitoredItemCreateRequest monRequest =
UA_MonitoredItemCreateRequest_default(newNode);
UA_Server_createDataChangeMonitoredItem(server, UA_TIMESTAMPSTORETURN_SOURCE,
monRequest, NULL, dataChangeNotificationCallback);
/* For creating Targetvariables */
UA_FieldTargetDataType_init(&targetVars[i].targetVariable);
targetVars[i].targetVariable.attributeId = UA_ATTRIBUTEID_VALUE;
targetVars[i].targetVariable.targetNodeId = newNode;
}
retval = UA_Server_DataSetReader_createTargetVariables(server, dataSetReaderId,
readerConfig.dataSetMetaData.fieldsSize, targetVars);
for(size_t i = 0; i < readerConfig.dataSetMetaData.fieldsSize; i++)
UA_FieldTargetDataType_clear(&targetVars[i].targetVariable);
UA_free(targetVars);
UA_free(readerConfig.dataSetMetaData.fields);
return retval;
}
/**
* **DataSetMetaData**
*
* The DataSetMetaData describes the content of a DataSet. It provides the information necessary to decode
* DataSetMessages on the Subscriber side. DataSetMessages received from the Publisher are decoded into
* DataSet and each field is updated in the Subscriber based on datatype match of TargetVariable fields of Subscriber
* and PublishedDataSetFields of Publisher */
/* Define MetaData for TargetVariables */
static void fillDataSetMetaData(UA_DataSetMetaDataType *pMetaData,
SubscribedVariable *subscribedVariableArray,
int variableNb) {
if(pMetaData == NULL) {
return;
}
UA_DataSetMetaDataType_init (pMetaData);
pMetaData->name = UA_STRING ("DataSet 1");
/* Definition of number of fields sizeto create different
* targetVariables of distinct datatype */
pMetaData->fieldsSize = variableNb;
UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND, "filedsSize %d", pMetaData->fieldsSize);
pMetaData->fields = (UA_FieldMetaData*)UA_Array_new (pMetaData->fieldsSize,
&UA_TYPES[UA_TYPES_FIELDMETADATA]);
for(size_t i = 0; i < pMetaData->fieldsSize; i++) {
UA_FieldMetaData_init (&pMetaData->fields[i]);
UA_NodeId_copy(&subscribedVariableArray[i].dataType,
&pMetaData->fields[i].dataType);
pMetaData->fields[i].builtInType = subscribedVariableArray[i].builtInType;
pMetaData->fields[i].name = subscribedVariableArray[i].name;
pMetaData->fields[i].valueRank = -1; /* scalar */
}
}
int subscribe(UA_String *transportProfile,
UA_NetworkAddressUrlDataType *networkAddressUrl,
UA_Boolean *running) {
const SubscribedVariable subscribedVariableArray[] = {
{
.builtInType = UA_NS0ID_DOUBLE,
.name = UA_STRING ("lattitude"),
.dataType = UA_TYPES[UA_TYPES_DOUBLE].typeId,
},
{
.builtInType = UA_NS0ID_DOUBLE,
.name = UA_STRING ("longitude"),
.dataType = UA_TYPES[UA_TYPES_DOUBLE].typeId,
},
{
.builtInType = UA_NS0ID_FLOAT,
.name = UA_STRING ("altitude"),
.dataType = UA_TYPES[UA_TYPES_FLOAT].typeId,
},
};
/* Return value initialized to Status Good */
UA_StatusCode retval = UA_STATUSCODE_GOOD;
UA_Server *server = UA_Server_new();
UA_ServerConfig *config = UA_Server_getConfig(server);
UA_ServerConfig_setMinimal(config, 4801, NULL);
/* Add the PubSub network layer implementation to the server config.
* The TransportLayer is acting as factory to create new connections
* on runtime. Details about the PubSubTransportLayer can be found inside the
* tutorial_pubsub_connection */
UA_ServerConfig_addPubSubTransportLayer(config, UA_PubSubTransportLayerUDPMP());
/* API calls */
/* Add PubSubConnection */
retval |= addPubSubConnection(server, transportProfile, networkAddressUrl);
if (retval != UA_STATUSCODE_GOOD)
return EXIT_FAILURE;
/* Add ReaderGroup to the created PubSubConnection */
retval |= addReaderGroup(server);
if (retval != UA_STATUSCODE_GOOD)
return EXIT_FAILURE;
/* Add DataSetReader to the created ReaderGroup */
retval |= addDataSetReader(server, subscribedVariableArray, countof(subscribedVariableArray));
if (retval != UA_STATUSCODE_GOOD)
return EXIT_FAILURE;
/* Add SubscribedVariables to the created DataSetReader */
retval |= addSubscribedVariables(server, readerIdentifier);
if (retval != UA_STATUSCODE_GOOD)
return EXIT_FAILURE;
retval = UA_Server_run(server, running);
UA_Server_delete(server);
return retval == UA_STATUSCODE_GOOD ? EXIT_SUCCESS : EXIT_FAILURE;
}
#include <quickjs/quickjs.h>
#include "mavsdk_wrapper.h"
#include "pubsub_publish.h"
#include "pubsub_subscribe.h"
static UA_Boolean publishing = true;
static UA_Boolean subscribing = true;
static JSValue js_pubsub_publish(JSContext *ctx, JSValueConst this_val,
int argc, JSValueConst *argv)
{
const char *ipv6;
const char *port;
char urlBuffer[44];
int res;
ipv6 = JS_ToCString(ctx, argv[0]);
port = JS_ToCString(ctx, argv[1]);
UA_snprintf(urlBuffer, sizeof(urlBuffer), "opc.udp://[%s]:%s/", ipv6, port);
UA_String transportProfile =
UA_STRING("http://opcfoundation.org/UA-Profile/Transport/pubsub-udp-uadp");
UA_NetworkAddressUrlDataType networkAddressUrl =
{UA_STRING_NULL , UA_STRING(urlBuffer)};
res = publish(&transportProfile, &networkAddressUrl, &publishing);
JS_FreeCString(ctx, ipv6);
JS_FreeCString(ctx, port);
return JS_NewInt32(ctx, res);
}
static JSValue js_pubsub_subscribe(JSContext *ctx, JSValueConst this_val,
int argc, JSValueConst *argv)
{
const char *ipv6;
const char *port;
char urlBuffer[44];
int res;
ipv6 = JS_ToCString(ctx, argv[0]);
port = JS_ToCString(ctx, argv[1]);
UA_snprintf(urlBuffer, sizeof(urlBuffer), "opc.udp://[%s]:%s/", ipv6, port);
UA_String transportProfile =
UA_STRING("http://opcfoundation.org/UA-Profile/Transport/pubsub-udp-uadp");
UA_NetworkAddressUrlDataType networkAddressUrl =
{UA_STRING_NULL , UA_STRING(urlBuffer)};
res = subscribe(&transportProfile, &networkAddressUrl, &subscribing);
JS_FreeCString(ctx, ipv6);
JS_FreeCString(ctx, port);
return JS_NewInt32(ctx, res);
}
void pubsub_set_coordinates(double lattitude, double longitude, float altitude)
{
writeDouble("lattitude", lattitude);
writeDouble("longitude", longitude);
writeFloat("altitude", altitude);
}
static JSValue js_stop_publishing(JSContext *ctx, JSValueConst this_val,
int argc, JSValueConst *argv)
{
publishing = false;
return JS_NewInt32(ctx, 0);
}
static JSValue js_stop_subscribing(JSContext *ctx, JSValueConst this_val,
int argc, JSValueConst *argv)
{
subscribing = false;
return JS_NewInt32(ctx, 0);
}
static JSValue js_mavsdk_start(JSContext *ctx, JSValueConst this_val,
int argc, JSValueConst *argv)
{
const char *url;
const char *log_file;
int timeout;
int res;
url = JS_ToCString(ctx, argv[0]);
log_file = JS_ToCString(ctx, argv[1]);
if (JS_ToInt32(ctx, &timeout, argv[2]))
return JS_EXCEPTION;
res = mavsdk_start(url, log_file, timeout, pubsub_set_coordinates);
JS_FreeCString(ctx, url);
JS_FreeCString(ctx, log_file);
return JS_NewInt32(ctx, res);
}
static JSValue js_mavsdk_stop(JSContext *ctx, JSValueConst this_val,
int argc, JSValueConst *argv)
{
return JS_NewInt32(ctx, mavsdk_stop());
}
static JSValue js_mavsdk_reboot(JSContext *ctx, JSValueConst this_val,
int argc, JSValueConst *argv)
{
return JS_NewInt32(ctx, mavsdk_reboot());
}
static JSValue js_mavsdk_healthAllOk(JSContext *ctx, JSValueConst this_val,
int argc, JSValueConst *argv)
{
return JS_NewBool(ctx, mavsdk_healthAllOk());
}
static JSValue js_mavsdk_landed(JSContext *ctx, JSValueConst this_val,
int argc, JSValueConst *argv)
{
return JS_NewBool(ctx, mavsdk_landed());
}
static JSValue js_mavsdk_arm(JSContext *ctx, JSValueConst this_val,
int argc, JSValueConst *argv)
{
return JS_NewInt32(ctx, mavsdk_arm());
}
static JSValue js_mavsdk_setTargetCoordinates(JSContext *ctx,
JSValueConst this_val,
int argc, JSValueConst *argv)
{
double la_arg_double;
double lo_arg_double;
double a_arg_double;
double y_arg_double;
if (JS_ToFloat64(ctx, &la_arg_double, argv[0]))
return JS_EXCEPTION;
if (JS_ToFloat64(ctx, &lo_arg_double, argv[1]))
return JS_EXCEPTION;
if (JS_ToFloat64(ctx, &a_arg_double, argv[2]))
return JS_EXCEPTION;
if (JS_ToFloat64(ctx, &y_arg_double, argv[3]))
return JS_EXCEPTION;
return JS_NewInt32(ctx, mavsdk_setTargetCoordinates(la_arg_double,
lo_arg_double,
a_arg_double,
y_arg_double));
}
static JSValue js_mavsdk_setTargetLatLong(JSContext *ctx, JSValueConst this_val,
int argc, JSValueConst *argv)
{
double la_arg_double;
double lo_arg_double;
if (JS_ToFloat64(ctx, &la_arg_double, argv[0]))
return JS_EXCEPTION;
if (JS_ToFloat64(ctx, &lo_arg_double, argv[1]))
return JS_EXCEPTION;
return JS_NewInt32(ctx, mavsdk_setTargetLatLong(la_arg_double,
lo_arg_double));
}
static JSValue js_mavsdk_setAltitude(JSContext *ctx, JSValueConst this_val,
int argc, JSValueConst *argv)
{
double altitude;
if (JS_ToFloat64(ctx, &altitude, argv[0]))
return JS_EXCEPTION;
return JS_NewInt32(ctx, mavsdk_setAltitude(altitude));
}
static JSValue js_mavsdk_setAirspeed(JSContext *ctx, JSValueConst this_val,
int argc, JSValueConst *argv)
{
double altitude;
if (JS_ToFloat64(ctx, &altitude, argv[0]))
return JS_EXCEPTION;
return JS_NewInt32(ctx, mavsdk_setAirspeed(altitude));
}
static JSValue js_mavsdk_getRoll(JSContext *ctx, JSValueConst this_val,
int argc, JSValueConst *argv)
{
return JS_NewFloat64(ctx, mavsdk_getRoll());
}
static JSValue js_mavsdk_getPitch(JSContext *ctx, JSValueConst this_val,
int argc, JSValueConst *argv)
{
return JS_NewFloat64(ctx, mavsdk_getPitch());
}
static JSValue js_mavsdk_getYaw(JSContext *ctx, JSValueConst this_val,
int argc, JSValueConst *argv)
{
return JS_NewFloat64(ctx, mavsdk_getYaw());
}
static JSValue js_mavsdk_getInitialLatitude(JSContext *ctx,
JSValueConst this_val,
int argc, JSValueConst *argv)
{
return JS_NewFloat64(ctx, mavsdk_getInitialLatitude());
}
static JSValue js_mavsdk_getLatitude(JSContext *ctx, JSValueConst this_val,
int argc, JSValueConst *argv)
{
return JS_NewFloat64(ctx, mavsdk_getLatitude());
}
static JSValue js_mavsdk_getInitialLongitude(JSContext *ctx,
JSValueConst this_val,
int argc, JSValueConst *argv)
{
return JS_NewFloat64(ctx, mavsdk_getInitialLongitude());
}
static JSValue js_mavsdk_getLongitude(JSContext *ctx, JSValueConst this_val,
int argc, JSValueConst *argv)
{
return JS_NewFloat64(ctx, mavsdk_getLongitude());
}
static JSValue js_mavsdk_getTakeOffAltitude(JSContext *ctx,
JSValueConst this_val,
int argc, JSValueConst *argv)
{
return JS_NewFloat64(ctx, mavsdk_getTakeOffAltitude());
}
static JSValue js_mavsdk_getInitialAltitude(JSContext *ctx,
JSValueConst this_val,
int argc, JSValueConst *argv)
{
return JS_NewFloat64(ctx, mavsdk_getInitialAltitude());
}
static JSValue js_mavsdk_getAltitude(JSContext *ctx, JSValueConst this_val,
int argc, JSValueConst *argv)
{
return JS_NewFloat64(ctx, mavsdk_getAltitude());
}
static JSValue js_mavsdk_loiter(JSContext *ctx, JSValueConst this_val,
int argc, JSValueConst *argv)
{
JSValueConst options;
JSValue val;
double radius = 10;
if(argc >= 1) {
options = argv[1];
val = JS_GetPropertyStr(ctx, options, "radius");
if(JS_ToFloat64(ctx, &radius, val)) {
return JS_EXCEPTION;
}
JS_FreeValue(ctx, val);
}
return JS_NewInt32(ctx, mavsdk_loiter(radius));
}
static JSValue js_mavsdk_doParachute(JSContext *ctx, JSValueConst this_val,
int argc, JSValueConst *argv)
{
int param;
if (JS_ToInt32(ctx, &param, argv[0]))
return JS_EXCEPTION;
return JS_NewInt32(ctx, mavsdk_doParachute(param));
}
static JSValue js_mavsdk_setTargetCoordinatesXYZ(JSContext *ctx,
JSValueConst this_val,
int argc, JSValueConst *argv)
{
double x_arg_double;
double y_arg_double;
double z_arg_double;
if (JS_ToFloat64(ctx, &x_arg_double, argv[0]))
return JS_EXCEPTION;
if (JS_ToFloat64(ctx, &y_arg_double, argv[1]))
return JS_EXCEPTION;
if (JS_ToFloat64(ctx, &z_arg_double, argv[2]))
return JS_EXCEPTION;
return JS_NewInt32(ctx, mavsdk_setTargetCoordinatesXYZ(x_arg_double,
y_arg_double,
z_arg_double));
}
static JSValue js_mavsdk_setTargetAltitude(JSContext *ctx, JSValueConst this_val,
int argc, JSValueConst *argv)
{
double a_arg_double;
if (JS_ToFloat64(ctx, &a_arg_double, argv[0]))
return JS_EXCEPTION;
return JS_NewInt32(ctx, mavsdk_setTargetAltitude(a_arg_double));
}
static JSValue js_mavsdk_takeOff(JSContext *ctx, JSValueConst this_val,
int argc, JSValueConst *argv)
{
return JS_NewInt32(ctx, mavsdk_takeOff());
}
static JSValue js_mavsdk_takeOffAndWait(JSContext *ctx, JSValueConst this_val,
int argc, JSValueConst *argv)
{
return JS_NewInt32(ctx, mavsdk_takeOffAndWait());
}
static JSValue js_mavsdk_land(JSContext *ctx, JSValueConst this_val,
int argc, JSValueConst *argv)
{
return JS_NewInt32(ctx, mavsdk_land());
}
static const JSCFunctionListEntry js_mavsdk_funcs[] = {
JS_CFUNC_DEF("start", 3, js_mavsdk_start ),
JS_CFUNC_DEF("stop", 0, js_mavsdk_stop ),
JS_CFUNC_DEF("reboot", 0, js_mavsdk_reboot ),
JS_CFUNC_DEF("healthAllOk", 0, js_mavsdk_healthAllOk ),
JS_CFUNC_DEF("landed", 0, js_mavsdk_landed ),
JS_CFUNC_DEF("arm", 0, js_mavsdk_arm ),
JS_CFUNC_DEF("setTargetCoordinates", 4, js_mavsdk_setTargetCoordinates ),
JS_CFUNC_DEF("setTargetLatLong", 2, js_mavsdk_setTargetLatLong ),
JS_CFUNC_DEF("setAltitude", 1, js_mavsdk_setAltitude ),
JS_CFUNC_DEF("setAirspeed", 1, js_mavsdk_setAirspeed ),
JS_CFUNC_DEF("loiter", 0, js_mavsdk_loiter ),
JS_CFUNC_DEF("doParachute", 1, js_mavsdk_doParachute ),
JS_CFUNC_DEF("setTargetCoordinatesXYZ", 3, js_mavsdk_setTargetCoordinatesXYZ ),
JS_CFUNC_DEF("setTargetAltitude", 1, js_mavsdk_setTargetAltitude ),
JS_CFUNC_DEF("getRoll", 0, js_mavsdk_getRoll ),
JS_CFUNC_DEF("getPitch", 0, js_mavsdk_getPitch ),
JS_CFUNC_DEF("getYaw", 0, js_mavsdk_getYaw ),
JS_CFUNC_DEF("getInitialLatitude", 0, js_mavsdk_getInitialLatitude ),
JS_CFUNC_DEF("getLatitude", 0, js_mavsdk_getLatitude ),
JS_CFUNC_DEF("getInitialLongitude", 0, js_mavsdk_getInitialLongitude ),
JS_CFUNC_DEF("getLongitude", 0, js_mavsdk_getLongitude ),
JS_CFUNC_DEF("getTakeOffAltitude", 0, js_mavsdk_getTakeOffAltitude ),
JS_CFUNC_DEF("getInitialAltitude", 0, js_mavsdk_getInitialAltitude ),
JS_CFUNC_DEF("getAltitude", 0, js_mavsdk_getAltitude ),
JS_CFUNC_DEF("takeOff", 0, js_mavsdk_takeOff ),
JS_CFUNC_DEF("takeOffAndWait", 0, js_mavsdk_takeOffAndWait ),
JS_CFUNC_DEF("land", 0, js_mavsdk_land ),
JS_CFUNC_DEF("publish", 2, js_pubsub_publish ),
JS_CFUNC_DEF("subscribe", 2, js_pubsub_subscribe ),
JS_CFUNC_DEF("stopPublishing", 0, js_stop_publishing ),
JS_CFUNC_DEF("stopSubscribing", 0, js_stop_subscribing ),
};
static int js_mavsdk_init(JSContext *ctx, JSModuleDef *m)
{
return JS_SetModuleExportList(ctx, m, js_mavsdk_funcs,
countof(js_mavsdk_funcs));
}
JSModuleDef *js_init_module(JSContext *ctx, const char *module_name)
{
JSModuleDef *m;
m = JS_NewCModule(ctx, module_name, js_mavsdk_init);
if (!m)
return NULL;
JS_AddModuleExportList(ctx, m, js_mavsdk_funcs, countof(js_mavsdk_funcs));
return m;
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment