Skip to content
Snippets Groups Projects
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
maoxdb.cpp 8.44 KiB
/*
 * ** header v3.0
 * This file is a part of the CaosDB Project.
 *
 * Copyright (C) 2021 IndiScale GmbH <info@indiscale.com>
 * Copyright (C) 2021 Daniel Hornung <d.hornung@indiscale.com>
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program. If not, see <https://www.gnu.org/licenses/>.
 *
 * ** end header
 */

/**
 * Mostly utility functions for MEX files interacting with libcaosdb.
 */

#include "maoxdb.hpp"
#include "mex.h"
#include "caosdb/status_code.h"
#include "caosdb/transaction.h"
#include "caosdb/transaction_status.h"

namespace maoxdb {
using std::string;

auto mxFromCaosDBParents(const caosdb::entity::Parents &parents) -> mxArray *;
auto mxFromCaosDBProperties(const caosdb::entity::Properties &properties) -> mxArray *;
auto mxFromCaosDBMessages(const caosdb::entity::Messages &messages) -> mxArray *;

// Exception and error handling ///////////////////////////////////////////////

/**
 * Generate a mex error message from a TransactionStatus.
 */
auto transactionStatusToMessage(
  const caosdb::transaction::Transaction *const trans)
  -> std::pair<string, string> {
  auto status = trans->GetStatus();
  auto code = status.GetCode();
  if (!status.IsError()) {
    return std::pair<string, string>();
  }
  string id = std::to_string(code);
  string text = status.GetDescription();
  return std::pair<string, string>(id, text);
}

/**
 * Throw an Octave error if the transaction did not finish properly.
 */
void throwOctExceptionIfError(
  const caosdb::transaction::Transaction *const trans) {
  auto msg = maoxdb::transactionStatusToMessage(trans);
  if (msg.first != "") {
    mexErrMsgIdAndTxt(msg.first.c_str(), msg.second.c_str());
  }
}

/**
 * Extract useful strings from an Exception: ID and description.
 */
auto exceptionToMessage(const caosdb::exceptions::Exception &exc)
  -> std::pair<string, string> {

  string id = std::to_string(exc.GetCode());
  string text =
    caosdb::get_status_description(exc.GetCode()) + "\n" + exc.what();

  return std::pair<string, string>(id, text);
}

/**
 * Just throw the error.
 */
void throwOctException(const caosdb::exceptions::Exception &exc) {
  std::pair<string, string> excContent = exceptionToMessage(exc);
  mexErrMsgIdAndTxt(excContent.first.c_str(), excContent.second.c_str());
}

// Entity handling ////////////////////////////////////////////////////////////

/**
 * @brief Convert a ResultSet to a struct mexArray.
 */
auto mxFromResultSet(const caosdb::transaction::ResultSet &resultSet)
  -> mxArray * {
  auto resSize = resultSet.Size();
  std::array<mwSize, 2> dims = {1, resSize};
  std::vector<const mxArray *> entities;
  // Obtain entities
  for (auto entity : resultSet) {
    entities.push_back(mxFromCaosDBEntity(entity));
  }

  auto * result = mxMergeScalarStructs(entities);

  return result;
}

/**
 * @brief Convert an Entity from libcaosdb to Octave struct mexArray.
 */
auto mxFromCaosDBEntity(const caosdb::entity::Entity &entity) -> mxArray * {
  auto fields = std::vector<const char*>
    {"role",
     "id",
     "versionId",
     "name",
     "description",
     "datatype",
     "unit",
     "value",
     "parents",
     "properties",
     "errors",
     "warnings",
     "infos"
    };
  std::array<mwSize, 2> dims = {1, (mwSize)fields.size()};
  auto * result = mxCreateStructArray(2, dims.data(), fields.size(), fields.data());
  // Fill with scalar values
  mxSetField(result, 0, "role", mxCreateString(entity.GetRole().c_str()));
  mxSetField(result, 0, "id", mxCreateString(entity.GetId().c_str()));
  mxSetField(result, 0, "versionId", mxCreateString(entity.GetVersionId().c_str()));
  mxSetField(result, 0, "name", mxCreateString(entity.GetName().c_str()));
  mxSetField(result, 0, "description", mxCreateString(entity.GetDescription().c_str()));
  mxSetField(result, 0, "datatype", mxCreateString(entity.GetDatatype().c_str()));
  mxSetField(result, 0, "unit", mxCreateString(entity.GetUnit().c_str()));
  // Parse value to proper type.
  mxSetField(result, 0, "value", mxScalarFromStringValue(entity));

  // Parents and Properties
  mxSetField(result, 0, "parents", mxFromCaosDBParents(entity.GetParents()));
  mxSetField(result, 0, "properties", mxFromCaosDBProperties(entity.GetProperties()));

  // message type content
  mxSetField(result, 0, "errors", mxFromCaosDBMessages(entity.GetErrors()));
  mxSetField(result, 0, "warnings", mxFromCaosDBMessages(entity.GetWarnings()));
  mxSetField(result, 0, "infos", mxFromCaosDBMessages(entity.GetInfos()));


  return result;
}

// Parents to struct array
auto mxFromCaosDBParents(const caosdb::entity::Parents &parents) -> mxArray * {
  std::vector<const char*> fields =
    {"id",
     "name",
     "description"
    };
  std::array<mwSize, 2> fieldDims = {1, (mwSize)fields.size()};
  std::array<mwSize, 2> dims = {1, (mwSize)parents.Size()};
  auto * result = mxCreateStructArray (2, dims.data(), fields.size(), fields.data());
  for (size_t i = 0; i < parents.Size(); ++i) {
    auto parent = parents.At(i);
    mxSetField(result, i, "id", mxCreateString(parent.GetId().c_str()));
    mxSetField(result, i, "name", mxCreateString(parent.GetName().c_str()));
    // FIXME Add again once upstream is ready.
    // mxSetField(result, i, "description", mxCreateString(parent.GetDescription().c_str()));
  }
  return result;
}

// Properties to struct array
auto mxFromCaosDBProperties(const caosdb::entity::Properties &properties) -> mxArray * {
  std::vector<const char*> fields =
    {"id",
     "name",
     "description",
     "importance",
     "value",
     "unit",
     "datatype"
    };
  std::array<mwSize, 2> fieldDims = {1, (mwSize)fields.size()};
  std::array<mwSize, 2> dims = {1, (mwSize)properties.Size()};
  auto * result = mxCreateStructArray (2, dims.data(), fields.size(), fields.data());
  for (mwIndex i = 0; i < properties.Size(); ++i) {
    auto property = properties.At(i);
    mxSetField(result, i, "id", mxCreateString(property.GetId().c_str()));
    mxSetField(result, i, "name", mxCreateString(property.GetName().c_str()));
    mxSetField(result, i, "description", mxCreateString(property.GetDescription().c_str()));
    mxSetField(result, i, "importance", mxCreateString(property.GetImportance().c_str()));
    mxSetField(result, i, "unit", mxCreateString(property.GetUnit().c_str()));
    mxSetField(result, i, "datatype", mxCreateString(property.GetDatatype().c_str()));
    // Parse value to proper type.
    mxSetField(result, i, "value", mxScalarFromStringValue(property));
  }
  return result;
}

// Messages to struct array
auto mxFromCaosDBMessages(const caosdb::entity::Messages &messages) -> mxArray * {
  std::vector<const char*> fields =
    {"code",
     "description"
    };
  std::array<mwSize, 2> fieldDims = {1, (mwSize)fields.size()};
  std::array<mwSize, 2> dims = {1, (mwSize)messages.Size()};
  auto * result = mxCreateStructArray (2, dims.data(), fields.size(), fields.data());
  for (mwIndex i = 0; i < messages.Size(); ++i) {
    auto message = messages.At(i);
    mxSetField(result, i, "code", mxScalarINT64(message.GetCode()));
    mxSetField(result, i, "description", mxCreateString(message.GetDescription().c_str()));
  }
  return result;
}

// Utility functions //////////////////////////////////////////////////////////

/**
 * @brief      Merges a number of scalar mex structs into a 1xN struct.
 */
auto mxMergeScalarStructs(const std::vector<const mxArray *> &structs) -> mxArray* {

  // We need the field names first to create a new struct
  auto nFields = (size_t)mxGetNumberOfFields(structs[0]);
  auto fields = std::vector<const char*>(nFields);
  for (mwIndex i = 0; i < nFields; ++i) {
    fields[i] = mxGetFieldNameByNumber(structs[0], i);
  }

  auto dims = std::array<mwSize, 2>{1, (mwSize)structs.size()};
  auto result = mxCreateStructArray(2, dims.data(), fields.size(), fields.data());

  auto i = mwIndex(0) - 1;
  for (auto scalarStruct: structs) {
    ++i;
    for (auto field: fields) {
      mxSetField(result, i, field, mxGetField(scalarStruct, 0, field));
    }
  }
  return result;
}

} // namespace maoxdb