From dd4d832773cbbf9f0f39da8265d31897753e906c Mon Sep 17 00:00:00 2001 From: garciay Date: Mon, 6 May 2019 07:31:51 -0700 Subject: [PATCH] Create project structure --- ccsrc/EncDec/LibHttp/LibItsHttp_Encdec.cc | 28 + .../LibMec/LocationAPI/LibMec_EncDec.cc | 37 + ccsrc/Framework/include/base_time.hh | 62 + ccsrc/Framework/include/codec.hh | 63 + ccsrc/Framework/include/codec_factory.hh | 46 + .../Framework/include/codec_stack_builder.hh | 74 + ccsrc/Framework/include/converter.hh | 406 ++++ ccsrc/Framework/include/layer.hh | 144 ++ ccsrc/Framework/include/layer_factory.hh | 114 ++ .../Framework/include/layer_stack_builder.hh | 67 + ccsrc/Framework/include/loggers.hh | 289 +++ ccsrc/Framework/include/params.hh | 95 + ccsrc/Framework/include/t_layer.hh | 72 + ccsrc/Framework/src/base_time.cc | 13 + ccsrc/Framework/src/codec_stack_builder.cc | 4 + ccsrc/Framework/src/converter.cc | 202 ++ ccsrc/Framework/src/layer_factory.cc | 71 + ccsrc/Framework/src/loggers.cc | 4 + ccsrc/Framework/src/params.cc | 99 + ccsrc/Ports/LibHttp/HttpPort.cc | 113 ++ ccsrc/Ports/LibHttp/HttpPort.hh | 46 + ccsrc/Protocols/ETH/ethernet_layer.cc | 82 + ccsrc/Protocols/ETH/ethernet_layer.hh | 48 + ccsrc/Protocols/ETH/ethernet_layer_factory.hh | 45 + ccsrc/Protocols/Http/http_codec.cc | 766 ++++++++ ccsrc/Protocols/Http/http_codec.hh | 67 + ccsrc/Protocols/Http/http_layer.cc | 129 ++ ccsrc/Protocols/Http/http_layer.hh | 83 + ccsrc/Protocols/Http/http_layer_factory.hh | 45 + ccsrc/Protocols/Json/json_codec.cc | 64 + ccsrc/Protocols/Json/json_codec.hh | 23 + ccsrc/Protocols/Json/json_codec_factory.hh | 46 + ccsrc/Protocols/Pcap/pcap_cygwin_layer.cc | 263 +++ ccsrc/Protocols/Pcap/pcap_cygwin_layer.hh | 78 + ccsrc/Protocols/Pcap/pcap_layer.cc | 276 +++ ccsrc/Protocols/Pcap/pcap_layer.hh | 5 + ccsrc/Protocols/Pcap/pcap_layer_factory.hh | 45 + ccsrc/Protocols/Pcap/pcap_linux_layer.cc | 199 ++ ccsrc/Protocols/Pcap/pcap_linux_layer.hh | 62 + ccsrc/Protocols/Pcap/pcap_offline_layer.cc | 228 +++ ccsrc/Protocols/Pcap/pcap_offline_layer.hh | 78 + .../Pcap/pcap_offline_layer_factory.hh | 45 + ccsrc/Protocols/Tcp/tcp_layer.cc | 203 ++ ccsrc/Protocols/Tcp/tcp_layer.hh | 99 + ccsrc/Protocols/Tcp/tcp_layer_factory.hh | 44 + docker/Dockerfile | 131 ++ docker/build-container.sh | 30 + docker/run-container.sh | 15 + docker/validate-in-docker.sh | 0 docs/o2.cfg | 0 etc/AtsMec/AtsMec_LocationAPI.cfg | 71 + etc/TestCodec/TestCodec.cfg | 69 + scripts/build_titan.bash | 103 ++ scripts/devenv.bash.ubuntu | 69 + scripts/mec_generate_makefile.bash | 235 +++ scripts/merge_mec_project.bash | 240 +++ scripts/run_all.bash | 47 + scripts/run_mtc.bash | 55 + scripts/run_mtc_simu.bash | 55 + scripts/run_ptcs.bash | 38 + scripts/run_ptcs_simu.bash | 20 + scripts/run_tshark.bash | 7 + scripts/testcodec_generate_makefile.bash | 242 +++ scripts/titan_repos.txt | 42 + scripts/update_mec_project.bash | 187 ++ ttcn/AtsMec/AtsMec_LocationAPI_TestCases.ttcn | 97 + ttcn/AtsMec/AtsMec_TestControl.ttcn | 21 + ttcn/LibMec/LocationAPI/json/LocationAPI.json | 1641 +++++++++++++++++ .../ttcn/LocationAPI_EncdecDeclarations.ttcn | 15 + .../LocationAPI/ttcn/LocationAPI_Pics.ttcn | 5 + .../LocationAPI/ttcn/LocationAPI_Pixits.ttcn | 25 + .../ttcn/LocationAPI_Templates.ttcn | 94 + .../ttcn/LocationAPI_TypesAndValues.ttcn | 50 + ttcn/LibMec/ttcn/LibMec_Functions.ttcn | 111 ++ ttcn/LibMec/ttcn/LibMec_Pics.ttcn | 10 + ttcn/LibMec/ttcn/LibMec_Pixits.ttcn | 3 + ttcn/TestCodec/TestCodec_External.ttcn | 40 + ttcn/TestCodec/TestCodec_TestAndSystem.ttcn | 9 + .../LibCommon_Sync.ttcn | 1358 ++++++++++++++ .../LibItsHttp_BinaryMessageBodyTypes.ttcn | 26 + .../LibItsHttp_BinaryTemplates.ttcn | 32 + .../LibItsHttp_BinaryTypes.ttcn | 9 + .../LibItsHttp_JsonMessageBodyTypes.ttcn | 23 + .../LibItsHttp_JsonTemplates.ttcn | 59 + vagrant/Vagrantfile | 73 + vagrant/provisioner.bash | 154 ++ 86 files changed, 10583 insertions(+) create mode 100644 ccsrc/EncDec/LibHttp/LibItsHttp_Encdec.cc create mode 100644 ccsrc/EncDec/LibMec/LocationAPI/LibMec_EncDec.cc create mode 100644 ccsrc/Framework/include/base_time.hh create mode 100644 ccsrc/Framework/include/codec.hh create mode 100644 ccsrc/Framework/include/codec_factory.hh create mode 100644 ccsrc/Framework/include/codec_stack_builder.hh create mode 100644 ccsrc/Framework/include/converter.hh create mode 100644 ccsrc/Framework/include/layer.hh create mode 100644 ccsrc/Framework/include/layer_factory.hh create mode 100644 ccsrc/Framework/include/layer_stack_builder.hh create mode 100644 ccsrc/Framework/include/loggers.hh create mode 100644 ccsrc/Framework/include/params.hh create mode 100644 ccsrc/Framework/include/t_layer.hh create mode 100644 ccsrc/Framework/src/base_time.cc create mode 100644 ccsrc/Framework/src/codec_stack_builder.cc create mode 100644 ccsrc/Framework/src/converter.cc create mode 100644 ccsrc/Framework/src/layer_factory.cc create mode 100644 ccsrc/Framework/src/loggers.cc create mode 100644 ccsrc/Framework/src/params.cc create mode 100644 ccsrc/Ports/LibHttp/HttpPort.cc create mode 100644 ccsrc/Ports/LibHttp/HttpPort.hh create mode 100644 ccsrc/Protocols/ETH/ethernet_layer.cc create mode 100644 ccsrc/Protocols/ETH/ethernet_layer.hh create mode 100644 ccsrc/Protocols/ETH/ethernet_layer_factory.hh create mode 100644 ccsrc/Protocols/Http/http_codec.cc create mode 100644 ccsrc/Protocols/Http/http_codec.hh create mode 100644 ccsrc/Protocols/Http/http_layer.cc create mode 100644 ccsrc/Protocols/Http/http_layer.hh create mode 100644 ccsrc/Protocols/Http/http_layer_factory.hh create mode 100644 ccsrc/Protocols/Json/json_codec.cc create mode 100644 ccsrc/Protocols/Json/json_codec.hh create mode 100644 ccsrc/Protocols/Json/json_codec_factory.hh create mode 100644 ccsrc/Protocols/Pcap/pcap_cygwin_layer.cc create mode 100644 ccsrc/Protocols/Pcap/pcap_cygwin_layer.hh create mode 100644 ccsrc/Protocols/Pcap/pcap_layer.cc create mode 100644 ccsrc/Protocols/Pcap/pcap_layer.hh create mode 100644 ccsrc/Protocols/Pcap/pcap_layer_factory.hh create mode 100644 ccsrc/Protocols/Pcap/pcap_linux_layer.cc create mode 100644 ccsrc/Protocols/Pcap/pcap_linux_layer.hh create mode 100644 ccsrc/Protocols/Pcap/pcap_offline_layer.cc create mode 100644 ccsrc/Protocols/Pcap/pcap_offline_layer.hh create mode 100644 ccsrc/Protocols/Pcap/pcap_offline_layer_factory.hh create mode 100644 ccsrc/Protocols/Tcp/tcp_layer.cc create mode 100644 ccsrc/Protocols/Tcp/tcp_layer.hh create mode 100644 ccsrc/Protocols/Tcp/tcp_layer_factory.hh create mode 100644 docker/Dockerfile create mode 100755 docker/build-container.sh create mode 100755 docker/run-container.sh create mode 100755 docker/validate-in-docker.sh create mode 100644 docs/o2.cfg create mode 100644 etc/AtsMec/AtsMec_LocationAPI.cfg create mode 100644 etc/TestCodec/TestCodec.cfg create mode 100755 scripts/build_titan.bash create mode 100755 scripts/devenv.bash.ubuntu create mode 100755 scripts/mec_generate_makefile.bash create mode 100755 scripts/merge_mec_project.bash create mode 100755 scripts/run_all.bash create mode 100755 scripts/run_mtc.bash create mode 100755 scripts/run_mtc_simu.bash create mode 100755 scripts/run_ptcs.bash create mode 100755 scripts/run_ptcs_simu.bash create mode 100755 scripts/run_tshark.bash create mode 100755 scripts/testcodec_generate_makefile.bash create mode 100644 scripts/titan_repos.txt create mode 100755 scripts/update_mec_project.bash create mode 100644 ttcn/AtsMec/AtsMec_LocationAPI_TestCases.ttcn create mode 100644 ttcn/AtsMec/AtsMec_TestControl.ttcn create mode 100644 ttcn/LibMec/LocationAPI/json/LocationAPI.json create mode 100644 ttcn/LibMec/LocationAPI/ttcn/LocationAPI_EncdecDeclarations.ttcn create mode 100644 ttcn/LibMec/LocationAPI/ttcn/LocationAPI_Pics.ttcn create mode 100644 ttcn/LibMec/LocationAPI/ttcn/LocationAPI_Pixits.ttcn create mode 100644 ttcn/LibMec/LocationAPI/ttcn/LocationAPI_Templates.ttcn create mode 100644 ttcn/LibMec/LocationAPI/ttcn/LocationAPI_TypesAndValues.ttcn create mode 100644 ttcn/LibMec/ttcn/LibMec_Functions.ttcn create mode 100644 ttcn/LibMec/ttcn/LibMec_Pics.ttcn create mode 100644 ttcn/LibMec/ttcn/LibMec_Pixits.ttcn create mode 100644 ttcn/TestCodec/TestCodec_External.ttcn create mode 100644 ttcn/TestCodec/TestCodec_TestAndSystem.ttcn create mode 100644 ttcn/patch_lib_common_titan/LibCommon_Sync.ttcn create mode 100644 ttcn/patch_lib_http/LibItsHttp_BinaryMessageBodyTypes.ttcn create mode 100644 ttcn/patch_lib_http/LibItsHttp_BinaryTemplates.ttcn create mode 100644 ttcn/patch_lib_http/LibItsHttp_BinaryTypes.ttcn create mode 100644 ttcn/patch_lib_http/LibItsHttp_JsonMessageBodyTypes.ttcn create mode 100644 ttcn/patch_lib_http/LibItsHttp_JsonTemplates.ttcn create mode 100644 vagrant/Vagrantfile create mode 100755 vagrant/provisioner.bash diff --git a/ccsrc/EncDec/LibHttp/LibItsHttp_Encdec.cc b/ccsrc/EncDec/LibHttp/LibItsHttp_Encdec.cc new file mode 100644 index 0000000..4eb8379 --- /dev/null +++ b/ccsrc/EncDec/LibHttp/LibItsHttp_Encdec.cc @@ -0,0 +1,28 @@ + +#include "LibItsHttp_MessageBodyTypes.hh" + +#include "http_codec.hh" + +#include "loggers.hh" + +namespace LibItsHttp__EncdecDeclarations { + + BITSTRING fx__enc__http__message(const LibItsHttp__TypesAndValues::HttpMessage& p) { + loggers::get_instance().log_msg(">>> fx__enc__http__message: ", (const Base_Type&)p); + + OCTETSTRING os; + http_codec codec; + codec.encode(p, os); + + return oct2bit(os); + } + INTEGER fx__dec__http__message(BITSTRING& pdu, LibItsHttp__TypesAndValues::HttpMessage& p) { + loggers::get_instance().log_msg(">>> fx__dec__http__message: ", pdu); + + OCTETSTRING os = bit2oct(pdu); + http_codec codec; + codec.decode(os, p); + + return 0; + } +} // End of namespace LibItsHttp__EncdecDeclarations diff --git a/ccsrc/EncDec/LibMec/LocationAPI/LibMec_EncDec.cc b/ccsrc/EncDec/LibMec/LocationAPI/LibMec_EncDec.cc new file mode 100644 index 0000000..79c510c --- /dev/null +++ b/ccsrc/EncDec/LibMec/LocationAPI/LibMec_EncDec.cc @@ -0,0 +1,37 @@ + +#include "LibMec_EncdecDeclarations.hh" + +#include "loggers.hh" + +namespace LibMec__EncdecDeclarations { + + BITSTRING fx__enc__LocationRequest__UserInfo(const LibMec__SIPTypesAndValues::Request& p) { + loggers::get_instance().log_msg(">>> fx__enc__UserInfo: ", p); + + float duration; + std::string tag("fx__enc__Request"); + loggers::get_instance().set_start_time(tag); + + OCTETSTRING os; +// sip_codec_request codec; +// if (codec.encode(p, os) == -1) { +// loggers::get_instance().warning("fx__enc__Request -1 result code was returned"); +// return int2bit(0, 1); +// } + loggers::get_instance().set_stop_time(tag, duration); + + return oct2bit(os); + } + + + INTEGER fx__dec__LocationRequest__UserInfo(BITSTRING& pdu, LibMec__SIPTypesAndValues::Request& p) { + loggers::get_instance().log_msg(">>> fx__dec__LocationRequest__UserInfo: ", pdu); + + OCTETSTRING os = bit2oct(pdu); +// sip_codec_request codec; +// codec.decode(os, p); + + return 0; + } + +} // End of namespace LibMec__EncdecDeclarations diff --git a/ccsrc/Framework/include/base_time.hh b/ccsrc/Framework/include/base_time.hh new file mode 100644 index 0000000..78bf5f9 --- /dev/null +++ b/ccsrc/Framework/include/base_time.hh @@ -0,0 +1,62 @@ +/*! + * \file base_time.hh + * \brief Header file for the control port base_time functionality. + * \author ETSI STF525 + * \copyright ETSI Copyright Notification + * No part may be reproduced except as authorized by written permission. + * The copyright and the foregoing restriction extend to reproduction in all media. + * All rights reserved. + * \version 0.1 + */ +#pragma once + +#include + +/** + * \class base_time + * \brief This class provides time tools such as getting current time + */ +class base_time { + const unsigned long long its_base_time_ms = 1072915200000L; //! Base time 01/01/2004 12:00am in millseconds + + static base_time* _instance; +private: + base_time() { }; //! Can not be created manually +public: + static inline base_time& get_instance(); + + virtual ~base_time() { if (_instance != nullptr) delete _instance; }; + +public: + inline const unsigned long long get_current_time_ms() const; + inline const unsigned long long get_its_base_time_ms() const; + inline const unsigned long long get_its_current_time_ms() const; + inline const unsigned long long get_its_current_time_us() const; + inline const unsigned long long get_its_current_time_mod_ms() const; +}; // End of class base_time + +// static functions +base_time& base_time::get_instance() { + return (_instance != nullptr) ? *_instance : *(_instance = new base_time()); +} + +const unsigned long long base_time::get_current_time_ms() const { + return std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(); +} + +const unsigned long long base_time::get_its_base_time_ms() const { + return base_time::its_base_time_ms; +} + +const unsigned long long base_time::get_its_current_time_ms() const { + return std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count() - base_time::its_base_time_ms; +} + +const unsigned long long base_time::get_its_current_time_us() const { + return std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count() - base_time::its_base_time_ms * 1000; +} + +const unsigned long long base_time::get_its_current_time_mod_ms() const { + return (std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count() - base_time::its_base_time_ms) % 65536; +} + diff --git a/ccsrc/Framework/include/codec.hh b/ccsrc/Framework/include/codec.hh new file mode 100644 index 0000000..9a6f4b5 --- /dev/null +++ b/ccsrc/Framework/include/codec.hh @@ -0,0 +1,63 @@ +/*! + * \file codec.hh + * \brief Header file for ITS abstract codec definition. + * \author ETSI STF525 + * \copyright ETSI Copyright Notification + * No part may be reproduced except as authorized by written permission. + * The copyright and the foregoing restriction extend to reproduction in all media. + * All rights reserved. + * \version 0.1 + */ +#pragma once + +#include "params.hh" + +class OCTETSTRING; //! Declare TITAN class +class CHARSTRING; //! Declare TITAN class +class BITSTRING; //! Declare TITAN class + +/*! + * \class codec + * \brief This class provides the interface for all ITS codecs, include UT and AC codecs + * \abstract + */ +template +class codec { +protected: + params* _params; //! Reference to params stack + // \todo Use smart pointer std::unique_ptr + +public: //! \publicsection + /*! + * \fn codec(); + * \brief Default constructor + * \todo Remove logs + */ + explicit codec() : _params(nullptr) { }; + /*! + * \fn ~codec(); + * \brief Default destructor + * \virtual + * \todo Remove logs + */ + virtual ~codec() { }; + /*! + * \fn int encode(const TPDUEnc& msg, OCTETSTRING& data); + * \brief Encode typed message into an octet string + * \param[in] p_message The typed message to be encoded + * \param[out] p_data The encoding result + * \return 0 on success, -1 otherwise + * \pure + */ + virtual int encode(const TPDUEnc& p_message, OCTETSTRING& p_data) = 0; + /*! + * \fn int decode(const OCTETSTRING& p_, TPDUDec& p_message, params* p_params = NULL); + * \brief Encode typed message into an octet string format + * \param[in] p_data The message in its octet string + * \param[out] p_message The decoded typed message + * \return 0 on success, -1 otherwise + * \pure + */ + virtual int decode(const OCTETSTRING& p_, TPDUDec& p_message, params* p_params = NULL) = 0; +}; // End of class codec + diff --git a/ccsrc/Framework/include/codec_factory.hh b/ccsrc/Framework/include/codec_factory.hh new file mode 100644 index 0000000..cbded3b --- /dev/null +++ b/ccsrc/Framework/include/codec_factory.hh @@ -0,0 +1,46 @@ +/*! + * \file codec_factory.hh + * \brief Header file for ITS abstract protocol codec definition. + * \author ETSI STF525 + * \copyright ETSI Copyright Notification + * No part may be reproduced except as authorized by written permission. + * The copyright and the foregoing restriction extend to reproduction in all media. + * All rights reserved. + * \version 0.1 + */ +#pragma once + +#include +#include +#include +#include + +#include "codec.hh" + +class Record_Type; //! TITAN forward declaration + +/*! + * \class codec_factory + * \brief This class provides a factory class to create codec class instances + * \abstract + */ +class codec_factory { +public: //! \publicsection + /*! + * \fn codec(); + * \brief Default constructor + */ + codec_factory() { }; + /*! + * \fn codec* create_codec(const std::string & type, const std::string & param); + * \brief Create the codecs stack based on the provided codecs stack description (cf. remark) + * \param[in] p_type The provided codecs stack description + * \param[in] p_params Optional parameters + * \return 0 on success, -1 otherwise + * \remark The description below introduces codecs stack in case of ITS project: + * HTTP(codecs=xml:held_codec;html:html_codec,json:json_codec)/TCP(debug=1,server=httpbin.org,port=80,use_ssl=0) + * \pure + */ + virtual codec* create_codec() = 0; +}; // End of class codec_factory + diff --git a/ccsrc/Framework/include/codec_stack_builder.hh b/ccsrc/Framework/include/codec_stack_builder.hh new file mode 100644 index 0000000..1a4d430 --- /dev/null +++ b/ccsrc/Framework/include/codec_stack_builder.hh @@ -0,0 +1,74 @@ +/*! + * \file codec_stack_builder.hh + * \brief Header file for ITS protocol stack builder. + * \author ETSI STF525 + * \copyright ETSI Copyright Notification + * No part may be reproduced except as authorized by written permission. + * The copyright and the foregoing restriction extend to reproduction in all media. + * All rights reserved. + * \version 0.1 + */ +#pragma once + +#include "codec_factory.hh" + +class Record_Type; //! TITAN forward declaration + +/*! + * \class codec_stack_builder + * \brief This class provides a factory class to create Codec class instances + */ +class codec_stack_builder { +private: //! \privatesection + static codec_stack_builder* _instance; //! Smart pointer to the unique instance of the logger framework + std::map _codecs; //! The list of the registered \see t_codec factories + + /*! + * \brief Default constructor + * Create a new instance of the codec_stack_builder class + * \private + */ + codec_stack_builder() { }; // can not be created manually +public: //! \publicsection + /*! + * \fn codec_stack_builder* get_instance(); + * \brief Accessor for the unique instance of the logger framework + * \static + */ + static codec_stack_builder* get_instance() { return _instance ? _instance : _instance = new codec_stack_builder(); }; + + /*! + * \fn void register_codec_factory(const std::string & p_type, codec_factory* p_codec_factory); + * \brief Add a new codec factory + * \param[in] p_type The codec identifier (e.g. GN for the GeoNetworking codec...) + * \param[in] p_codec_factory A reference to the \see codec_factory + * \static + */ + static void register_codec_factory(const std::string & p_type, codec_factory* p_codec_factory) { codec_stack_builder::get_instance()->_register_codec_factory(p_type, p_codec_factory); }; + +private: //! \privatesection + /*! + * \fn void _register_codec_factory(const std::string & p_type, codec_factory* p_codec_factory); + * \brief Add a new codec factory + * \param[in] p_type The codec identifier (e.g. GN for the GeoNetworking codec...) + * \param[in] p_codec_factory A reference to the \see codec_factory + */ + void _register_codec_factory(const std::string & p_type, codec_factory* p_codec_factory) { _codecs[p_type] = p_codec_factory; }; + +public: //! \publicsection + /*! + * \fn codec* get_codec(const char* p_codec_name); + * \brief Retrieve the specified codec name from the list of the registered codecs + * \param[in] p_codec_name The codec indentifier + * \return The pointer to the codec object on success, nullptr otherwise + */ + inline codec* get_codec(const char* p_codec_name) { // NOTE A virtual method cannot not be a template ==> polymorphism required here + typename std::map::const_iterator it = _codecs.find(p_codec_name); + if (it != _codecs.cend()) { + return it->second->create_codec(); + } + + return nullptr; + } +}; // End of class codec_stack_builder + diff --git a/ccsrc/Framework/include/converter.hh b/ccsrc/Framework/include/converter.hh new file mode 100644 index 0000000..77800b5 --- /dev/null +++ b/ccsrc/Framework/include/converter.hh @@ -0,0 +1,406 @@ +/*! + * \file converter.hh + * \brief Helper class for types converter. + * \author ETSI STF525 + * \copyright ETSI Copyright Notification + * No part may be reproduced except as authorized by written permission. + * The copyright and the foregoing restriction extend to reproduction in all media. + * All rights reserved. + * \version 0.1 + */ +#pragma once + +#include +#include +#include + +#include +#include +#include + +#include +#include +#include // LONG_MAX, LLONG_MAX +#include // time_t, struct tm, difftime, time, mktime + +/*! + * \class converter + * \brief This class provide a set of methods for types conversions + * \remark Singleton pattern + */ +class converter { + + /*! + * \brief Unique static object reference of this class + */ + static converter * instance; + + /*! + * \brief Default private ctor + */ + converter() {}; + /*! + * \brief Default private dtor + */ + ~converter() { + if (instance != NULL) { + delete instance; + instance = NULL; + } + }; + +public: /*! \publicsection */ + /*! + * \brief Public accessor to the single object reference + */ + inline static converter & get_instance() { + if (instance == NULL) instance = new converter(); + return *instance; + }; + +public: + /*! + * \enum endian_t + * \brief Endianess style + */ + typedef enum { + big_endian, + little_endian + } endian_t; + +public: + /*! + * \brief Convert a Binary Coded Decimal value into a binary value + * \param[in] p_value The BDC value + * \return The binary value + * \inline + */ + inline uint8_t bcd_to_bin(const uint8_t p_value) { + return ((p_value / 16 * 10) + (p_value % 16)); + }; + + /*! + * \brief Convert a binary value into a Binary Coded Decimal value + * \param[in] p_value The binary value + * \return The BCD value + * \inline + */ + inline uint8_t bin_to_bcd(const uint8_t p_value) { + return ((p_value / 10 * 16) + (p_value % 10)); + }; + + /*! + * \brief Swap two bytes length value (e.g. 0xCAFE becomes 0xFECA) + * \param[in] p_value The value to swap + * \return The swapped value + * \inline + */ + uint16_t swap(const uint16_t p_value); + inline int16_t swap(const int16_t p_value) { + return static_cast(swap(static_cast(p_value))); + }; + /*! + * \brief Swap four bytes length value (used for littel endian / big endian) + * \param[in] p_value The value to swap + * \return The swapped value + */ + uint32_t swap(const uint32_t p_value); + inline int32_t swap(const int32_t p_value) { + return static_cast(swap(static_cast(p_value))); + }; + + /*! + * \brief Convert a string into an hexadecimal string + * \param[in] p_value The string value + * \return The hexadecimal value + */ + std::string string_to_hexa(const std::string & p_value, const bool p_uppercase = false); + /*! + * \brief Convert a bytes array int32_t an hexadecimal string + * \param[in] p_value The bytes array value + * \return The hexadecimal value + */ + std::string bytes_to_hexa(const std::vector & p_value, const bool p_uppercase = false); + /*! + * \brief Convert an hexadecimal string into a bytes array + * \param[in] p_value The hexadecimal value + * \return The bytes array value + */ + std::vector hexa_to_bytes(const std::string & p_value); + + /*! + * \brief Convert a time in time_t format into a string formated according to RFC 822, 1036, 1123, 2822 + * \param[in] p_time The time to convert in time_t format + * \return The time string formated + * \see http://www.unixtimestamp.com/ + * @code + * std::string result = time_to_string(1489755780); + * result.compare("Fri, 17 Mar 2017 13:03:00 +0000") == 0 // When time zone is set to UTC + * @endcode + * \remark Use commands 1) timedatectl to change your machine timezone (e.g. sudo timedatectl set-timezone UTC to change machine timezone to UTC, 2) timedatectl list-timezones to get the list of the timezones) + */ + std::string time_to_string(const time_t p_time); + /*! + * \brief Convert a time in struct tm format into a string formated according to RFC 822, 1036, 1123, 2822 + * \param[in] p_time The time to convert in struct tm format + * \return The time string formated + * \see http://www.unixtimestamp.com/ + */ + std::string time_to_string(const struct tm & p_time); + + /*! + * \brief Convert a 16-bits integer (int16_t) into a bytes array + * \param[in] p_value The 16-bits integer value + * \param[in] p_endianess Endianess style. Default: big_endian + * \return The bytes array value + */ + inline std::vector short_to_bytes(const int16_t p_value, const endian_t p_endianess = big_endian) const { + std::vector result(sizeof(short), 0x00); + for (int i = sizeof(short) - 1; i >= 0; i--) { + int offset = (sizeof(short) - 1 - i) * 8; + result[i] = static_cast((p_value >> offset) & 0xFF); + } // End of 'for' statement + return result; + }; // End of short_to_bytes + + /*! + * \brief Convert a bytes array into a 16-bits integer (int16_t) + * \param[in] p_value The bytes array + * \param[in] p_endianess Endianess style. Default: big_endian + * \return The 16-bits integer on success, SHRT_MAX on error (wrong bytes array size) + */ + inline int16_t bytes_to_short(const std::vector & p_value, const endian_t p_endianess = big_endian) const { + // Sanity check + if (p_value.size() > sizeof(short)) { + return SHRT_MAX; + } + int16_t value = 0; + for (size_t i = 0; i < p_value.size(); i++) { + value = (value << 8) + (p_value[i] & 0xff); + } // End of 'for' statement + return value; + }; // End of bytes_to_short + + /*! + * \brief Convert a 32-bits integer (int32_t) into a bytes array + * \param[in] p_value The 32-bits integer value + * \param[in] p_endianess Endianess style. Default: big_endian + * \return The bytes array value + */ + inline std::vector int_to_bytes(const int32_t p_value, const endian_t p_endianess = big_endian) const { + /*uint8_t bytes[sizeof(p_value)]; + std::copy( + static_cast(static_cast(&p_value)), + static_cast(static_cast(&p_value)) + sizeof(p_value), + bytes + ); + std::vector result(bytes, bytes + sizeof(bytes) / sizeof(uint8_t));*/ + std::vector result(sizeof(int), 0x00); + for (int i = sizeof(int) - 1; i >= 0; i--) { + int offset = (sizeof(int) - 1 - i) * 8; + result[i] = static_cast((p_value >> offset) & 0xFF); + } // End of 'for' statement + return result; + }; // End of int_to_bytes + + /*! + * \brief Convert a bytes array into a 32-bits integer (int32_t) + * \param[in] p_value The bytes array + * \param[in] p_endianess Endianess style. Default: big_endian + * \return The 32-bits integer on success, LONG_MAX on error (wrong bytes array size) + */ + inline int32_t bytes_to_int(const std::vector & p_value, const endian_t p_endianess = big_endian) const { + // Sanity check + if (p_value.size() > sizeof(int)) { + return INT_MAX; + } + int32_t value = 0; + for (size_t i = 0; i < p_value.size(); i++) { + value = (value << 8) + (p_value[i] & 0xff); + } // End of 'for' statement + return value; + // return *((int *)(&p_value[0])); + }; // End of bytes_to_int + + /*! + * \brief Convert a 64-bits integer (int64_t) into a bytes array + * \param[in] p_value The 64-bits integer value + * \param[in] p_endianess Endianess style. Default: big_endian + * \return The bytes array value + */ + inline std::vector long_to_bytes(const int64_t p_value, const endian_t p_endianess = big_endian) const { + /*uint8_t bytes[sizeof(p_value)]; + std::copy( + static_cast(static_cast(&p_value)), + static_cast(static_cast(&p_value)) + sizeof(p_value), + bytes + ); + std::vector result(bytes, bytes + sizeof(bytes) / sizeof(uint8_t));*/ + std::vector result(sizeof(int64_t), 0x00); + for (int i = sizeof(int64_t) - 1; i >= 0; i--) { + int offset = (sizeof(int64_t) - 1 - i) * 8; + result[i] = static_cast((p_value >> offset) & 0xFF); + } // End of 'for' statement + return result; + }; // End of long_to_bytes + + /*! + * \brief Convert a bytes array into a 64-bits integer (int64_t) + * \param[in] p_value The bytes array + * \param[in] p_endianess Endianess style. Default: big_endian + * \return The 64-bits integer on success, LLONG_MAX on error (wrong bytes array size) + */ + inline int64_t bytes_to_long(const std::vector & p_value, const endian_t p_endianess = big_endian) const { + // Sanity check + if (p_value.size() > sizeof(int64_t)) { + return LLONG_MAX; + } + int64_t value = 0; + for (size_t i = 0; i < p_value.size(); i++) { + value = (value << 8) + (p_value[i] & 0xff); + } // End of 'for' statement + return value; + // return *((long *)(&p_value[0])); + }; // End of bytes_to_long + + /*! + * \brief Convert a float value into a bytes array + * \param[in] p_value The float value + * \return The bytes array value + */ + inline std::vector float_to_bytes(const float p_value) const { + uint8_t bytes[sizeof(p_value)]; + std::copy( + static_cast(static_cast(&p_value)), + static_cast(static_cast(&p_value)) + sizeof(p_value), + bytes + ); + std::vector result(bytes, bytes + sizeof(bytes) / sizeof(uint8_t)); + return result; + }; // End of float_to_long + + /*! + * \brief Convert a bytes array into a float + * \param[in] p_value The bytes array + * \return The float value + */ + inline float bytes_to_float(const std::vector & p_value) const { + return *((float *)(&p_value[0])); + }; // End of bytes_to_float + + /*! + * \brief Convert a string into a bytes array + * \param[in] p_value The string value + * \return The bytes array value + */ + inline std::vector string_to_bytes(const std::string & p_value) const { + return std::vector(p_value.begin(), p_value.end()); + }; // End of string_to_bytes + + /*! + * \brief Convert a bytes array into a string + * \param[in] p_value The bytes array value + * \return The string value + */ + inline std::string bytes_to_string(const std::vector & p_value) const { + return std::string(p_value.begin(), p_value.end()); + }; // End of bytes_to_string + +public: + /*! + * \brief Convert a string into an integer + * \param[in] p_value The string value + * \return The integer value + */ + inline int32_t string_to_int(const std::string & p_value) const { + return std::stoi(p_value); + //return atoi(p_value.c_str()); + }; // End of string_to_int + + /*! + * \brief Convert an integer into a string + * \param[in] p_value The integer value + * \return The string value + */ + inline std::string int_to_string(const int32_t & p_value) const { + std::ostringstream ss; + ss << p_value; + return ss.str(); + }; // End of string_to_bytes + + /*! + * \brief Convert a string in to lower case + * \param[in/out] p_value The string value to convert + */ + inline void to_lower(std::string& p_value) { + std::transform(p_value.begin(), p_value.end(), p_value.begin(), ::tolower); + } + + /*! + * \brief Convert a string in to upper case + * \param[in/out] p_value The string value to convert + */ + inline void to_upper(std::string& p_value) { + std::transform(p_value.begin(), p_value.end(), p_value.begin(), ::toupper); + } + +public: + + /*! + * \brief Returns a copy of the string, with leading and trailing special characters omitted + * \param[in] p_value The string value + * \param[in] p_trim_chars The special characters to be omitted. Default: ' ' and TAB + * \return The new string value + */ + std::string trim(const std::string& p_value, const std::string& p_trim_chars = " \t"); + + /*! + * \brief Convert the provided string into a list of arguments + * \param[in] p_value The string value + * \param[in] p_separator The separator sequence to use for the spliting process + * \return The item list + * \code{.cc} + * std::string str = "This is a test for spliting a string with a white spave"; + * std::vector tokens = converter::get_instance().split(str, " "); + * std::clog << "Tokens: " << std::endl; + * for (auto it = tokens.begin(); it != tokens.end(); ++it) { + * std::clog << " " << *it << std::endl; + * } + * \endcode + */ + std::vector split(const std::string & p_value, const std::string& p_separator); + + /*! + * \brief Convert the provided string into a list of arguments + * \param[in] p_value The string value + * \return The arguments list + * \code{.cc} + * std::string str = "--host localhost --port 12345 --duration -1"; + * std::vector tokens = converter::get_instance().split_arguments_line(str); + * std::clog << "Tokens: " << std::endl; + * for (auto it = tokens.begin(); it != tokens.end(); ++it) { + * std::clog << " " << *it << std::endl; + * } + * \endcode + */ + std::vector split_arguments_line(const std::string & p_value); + + /*! + * \brief Convert the provided buffer into a Base64 + * \param[in] p_value The buffer value + * \return The Base64 encoded buffert + */ + std::vector buffer_to_base64(const std::vector & p_value); + + /*! + * \brief Convert the provided Base64 buffer + * \param[in] p_value The buffer value + * \return The Base64 encoded buffert + */ + std::vector base64_to_buffer(const std::vector & p_value); + + static const std::string lut_u; + static const std::string lut_l; + static const std::string base64_enc_map; + +}; // End of class converter diff --git a/ccsrc/Framework/include/layer.hh b/ccsrc/Framework/include/layer.hh new file mode 100644 index 0000000..abd4eae --- /dev/null +++ b/ccsrc/Framework/include/layer.hh @@ -0,0 +1,144 @@ +/*! + * \file layer.hh + * \brief Header file for ITS abstract protocol layer definition. + * \author ETSI STF525 + * \copyright ETSI Copyright Notification + * No part may be reproduced except as authorized by written permission. + * The copyright and the foregoing restriction extend to reproduction in all media. + * All rights reserved. + * \version 0.1 + */ +#pragma once + +#include +#include +#include +#include + +#include "params.hh" + +class OCTETSTRING; //! Forward declaration of TITAN class +class BITSTRING; //! Forward declaration of TITAN class +class CHARSTRING; //! Forward declaration of TITAN class +class INTEGER; //! Forward declaration of TITAN class + +/*! + * \class layer + * \brief This class provides basic description of an ITS protocol layer + */ +class layer { + std::vector upperLayers; //! List of the upper protocol layers + std::vector lowerLayers; //! List of the lower protocol layers + +protected: + std::string type; //! Type description, it indicates the protocol type (e.g. CAM, DENM, GN, ETH, PCAP...) + +public: //! \publicsection + /*! + * \brief Default constructor + * Create a new instance of the layer class + */ + explicit layer() : upperLayers(), lowerLayers(), type(std::string("")) { }; + + /*! + * \brief Specialized constructor + * Create a new instance of the layer class with its type description + * \param[in] p_type The port type name (e.g. GN for the GeoNetworking layer) + * \remark This constructor is called by the layer factory + * \see layer_factory + */ + explicit layer(const std::string& p_type) : upperLayers(), lowerLayers(), type(std::string(p_type.begin(), p_type.end())) { }; + + /*! + * \brief Default destructor + * \todo Remove logs + */ + virtual ~layer() { + // Double linked list, only remove layers in lowerLayers from the lowest one + std::for_each(lowerLayers.rbegin(), lowerLayers.rend(), [](layer* it) { delete it; } ); + lowerLayers.clear(); + upperLayers.clear(); + }; + + /*! + * \fn void delete_layer(); + * \brief Delete this layer + * \todo To be implemented + */ + void delete_layer() { }; + +public: //! \publicsection + /*! + * \inline + * \fn void add_upper_layer(layer* p_layer); + * \brief Add a new layer in the list of the upper layer + * \param[in] p_layer The layer protocol to be removed + */ + inline void add_upper_layer(layer* p_layer) { + if (p_layer != NULL) { + upperLayers.push_back(p_layer); + p_layer->lowerLayers.push_back(this); + }; + }; + + /*! + * \fn void remove_upper_layer(layer* p_layer); + * \brief Remove the specified upper layer protocol from the list of the upper layer + * \param[in] p_layer The layer protocol to be removed + * \todo To be implemented + */ + void remove_upper_layer(layer* p_layer) { }; + + /*! + * \virtual + * \fn void send_data(OCTETSTRING& data, params& params); + * \brief Send bytes formated data to the lower layers + * \param[in] p_data The data to be sent + * \param[in] p_params Some parameters to overwrite default value of the lower layers parameters + * \todo Remove the logs + * \virtual + */ + virtual void send_data(OCTETSTRING& p_data, params& p_params) { }; + + /*! + * \virtual + * \fn void receive_data(OCTETSTRING& data, params& params); + * \brief Receive bytes formated data from the lower layers + * \param[in] p_data The bytes formated data received + * \param[in] p_params Some lower layers parameters values when data was received + * \todo Remove the logs + * \virtual + */ + virtual void receive_data(OCTETSTRING& p_data, params& p_params) { } + + /*! + * \inline + * \fn const std::string& to_string(); + * \brief Remove the specified upper layer protocol from the list of the upper layer + * \param[in] The layer protocol to be removed + */ + inline const std::string& to_string() const { return type; }; + +protected: //! \protectedsection + inline void to_all_layers(std::vector&layers, OCTETSTRING& data, params& params) { + for (std::vector::const_iterator it = layers.cbegin(); it != layers.cend(); ++it) { + layer* p = *it; + p->receive_data(data, params); // FIXME BUG I + } // End of 'for' statement + }; + + inline void receive_to_all_layers(OCTETSTRING& data, params& params) { + for (std::vector::const_iterator it = upperLayers.cbegin(); it != upperLayers.cend(); ++it) { + layer* p = *it; + p->receive_data(data, params); + } // End of 'for' statement + }; + + inline void send_to_all_layers(OCTETSTRING& data, params& params) { + for (std::vector::const_iterator it = lowerLayers.cbegin(); it != lowerLayers.cend(); ++it) { + layer* p = *it; + p->send_data(data, params); + } // End of 'for' statement + }; +}; // End of class layer + diff --git a/ccsrc/Framework/include/layer_factory.hh b/ccsrc/Framework/include/layer_factory.hh new file mode 100644 index 0000000..c66fdc3 --- /dev/null +++ b/ccsrc/Framework/include/layer_factory.hh @@ -0,0 +1,114 @@ +/*! + * \file layer_factory.hh + * \brief Header file for ITS abstract protocol layer definition. + * \author ETSI STF525 + * \copyright ETSI Copyright Notification + * No part may be reproduced except as authorized by written permission. + * The copyright and the foregoing restriction extend to reproduction in all media. + * All rights reserved. + * \version 0.1 + */ +#pragma once + +#include +#include +#include +#include + +#include "layer.hh" + +/*! + * \class layer_factory + * \brief This class provides a factory class to create layer class instances + * \abstract + */ +class layer_factory { +public: //! \publicsection + /*! + * \fn codec(); + * \brief Default constructor + */ + layer_factory() {}; + /*! + * \fn layer* create_layer(const std::string & type, const std::string & param); + * \brief Create the layers stack based on the provided layers stack description (cf. remark) + * \param[in] p_type The provided layers stack description + * \param[in] p_params Optional parameters + * \return 0 on success, -1 otherwise + * \remark The description below introduces layers stack in case of ITS project: + * CAM layer + * next_header : btpA|btpB (overwrite BTP.type) + * header_type : tsb|gbc + * header_sub_type : sh (single hop) + * DENM layer + * next_header : btpA|btpB (overwrite BTP.type) + * header_type : tsb|gbc + * BTP layer + * type : btpA|btpB + * destination port: dst_port + * source port : src_port + * device_mode : Set to 1 if the layer shall encapsulate upper layer PDU + * GN layer + * its_aid : ITS AID as defined by ETSI TS 102 965 V1.2.1. Default: 141 + * ll_address : GeoNetworking address of the Test System + * latitude : latitude of the Test System + * longitude : longitude of the Test System + * beaconing : Set to 1 if GnLayer shall start beaconing + * Beaconing timer expiry : expiry (ms) + * device_mode : Set to 1 if the layer shall encapsulate upper layer PDU + * secured_mode : Set to 1 if message exchanges shall be signed + * encrypted_mode : Set to 1 if message exchanges shall be encrypted + * NOTE: For signed & encrypted message exchanges, both secured_mode and encrypted_mode shall be set to 1 + * certificate : Certificate identifier the Test Adapter shall use + * secure_db_path : Path to the certificates and keys storage location + * hash : Hash algorithm to be used when secured mode is set + * Authorized values are SHA-256 or SHA-384 + * Default: SHA-256 + * signature : Signature algorithm to be used when secured mode is set + * Authorized values are NISTP-256, BP-256 and BP-384 + * Default: NISTP-256 + * cypher : Cyphering algorithm to be used when secured mode is set + * Ethernet layer + * mac_src :Source MAC address + * mac_bc :Broadcast address + * eth_type : Ethernet type + * Commsignia layer + * mac_src : Device MAC address, used to discard packets + * To indicate no filering, use the value 000000000000 + * mac_bc : Broadcast address + * eth_type : Ethernet type, used to discard packets + * target_host : Device address + * target_port : Device port + * source_port : Test System port + * interface_id: Interface id, used to discard packets + * tx_power : TX power (dB) + * UDP layer (IP/UDP based on Pcap) + * dst_ip : destination IPv4 address (aa.bb.cc.dd) + * dst_port: destination port + * src_ip : source IPv4 address (aa.bb.cc.dd) + * src_port: source port + * Pcap layer + * mac_src : Source MAC address, used to exclude from capture the acket sent by the Test System + * filter : Pcap filter (compliant with tcpdump syntax) + * E.g. filter=and ether src 04e548000001 + * Online mode: + * nic: Local NIC + * If set, online mode is used + * Offline mode (nic is present but not set): + * file : File to read + * frame_offset: Frame offset, used to skip packets with frame number < frame_offset + * time_offset : Time offset, used to skip packets with time offset < time_offset + * save_mode : 1 to save sent packet, 0 otherwise + * Here are some examples: + * GeoNetworking multiple component case: + * NodeB.geoNetworkingPort.params := "GN(ll_address=04e548000001,latitude=43551050,longitude=10298730,beaconing=0,expiry=1000,its_aid=141)/COMMSIGNIA(mac_src=04e548000001,mac_bc=FFFFFFFFFFFF,eth_type=8947,target_host=10.200.1.101,target_port=7942,source_port=7943,its_aid=141,interface_id=2,tx_power=-32)/UDP(dst_ip=192.168.56.1,dst_port=12346,src_ip=192.168.156.4,src_port=12345)/ETH(mac_src=04e548000001,mac_dst=0A0027000011,eth_type=0800)/PCAP(mac_src=04e548000001,file=/home/vagrant/TriesAndDelete/etsi_its/testdata/TC_AUTO_IOT_DENM_RWW_BV_01_short.pcap,filter=and (udp port 30000 or udp port 7943))" +NodeC.geoNetworkingPort.params := "GN(ll_address=70b3d5791b48,latitude=43551050,longitude=10298730,beaconing=0,expiry=1000,its_aid=141)/COMMSIGNIA(mac_src=70b3d5791b48,mac_bc=FFFFFFFFFFFF,eth_type=8947,target_host=10.200.1.101,target_port=7942,source_port=7943,its_aid=141,interface_id=2,tx_power=-32)/UDP(dst_ip=192.168.56.1,dst_port=12346,src_ip=192.168.156.4,src_port=12345)/ETH(mac_src=70b3d5791b48,mac_dst=0A0027000011,eth_type=0800)/PCAP(mac_src=70b3d5791b48,file=/home/vagrant/TriesAndDelete/etsi_its/testdata/TC_AUTO_IOT_DENM_RWW_BV_01_short.pcap,filter=and (udp port 30000 or udp port 7943))" + * NodeB.geoNetworkingPort.params := "GN(ll_address=04e548000001,latitude=43551050,longitude=10298730,beaconing=0,expiry=1000,its_aid=141)/ETH(mac_src=04e548000001,mac_dst=0A0027000011,eth_type=0800)/PCAP(mac_src=04e548000001,file=/home/vagrant/TriesAndDelete/etsi_its/testdata/TC_AUTO_IOT_DENM_RWW_BV_01.pcap,filter=and ether src 04e548000001)" +#NodeC.geoNetworkingPort.params := "GN(ll_address=70b3d5791b48,latitude=43551050,longitude=10298730,beaconing=0,expiry=1000,its_aid=141)/ETH(mac_src=70b3d5791b48,mac_dst=0A0027000011,eth_type=0800)/PCAP(mac_src=70b3d5791b48,file=/home/vagrant/TriesAndDelete/etsi_its/testdata/TC_AUTO_IOT_DENM_RWW_BV_01.pcap,filter=and ether src 70b3d5791b48)" + * UpperTester port based on UDP + * system.utPort.params := "UT_GN/UDP(dst_ip=192.168.1.1,dst_port=12346,src_ip=192.168.156.4,src_port=12345)/ETH(mac_src=026f8338c1e5,mac_dst=0A0027000011,eth_type=0800)/PCAP(mac_src=0800275c4959,nic=enp0s8,filter=and udp port 12346)" + * \pure + */ + virtual layer* create_layer(const std::string & p_type, const std::string & p_params) = 0; +}; // End of class layer_factory + diff --git a/ccsrc/Framework/include/layer_stack_builder.hh b/ccsrc/Framework/include/layer_stack_builder.hh new file mode 100644 index 0000000..af797a3 --- /dev/null +++ b/ccsrc/Framework/include/layer_stack_builder.hh @@ -0,0 +1,67 @@ +/*! + * \file layer_stack_builder.hh + * \brief Header file for ITS protocol stack builder. + * \author ETSI STF525 + * \copyright ETSI Copyright Notification + * No part may be reproduced except as authorized by written permission. + * The copyright and the foregoing restriction extend to reproduction in all media. + * All rights reserved. + * \version 0.1 + */ +#pragma once + +#include "layer_factory.hh" + +/*! + * \class layer_stack_builder + * \brief This class provides a factory class to create Layer class instances + */ +class layer_stack_builder { +private: //! \privatesection + typedef std::map LayerFactoryMap; + + static layer_stack_builder * _instance; //! Smart pointer to the unique instance of the logger framework + std::map _layer_factories; //! The list of the registered \see t_layer factories + + /*! + * \brief Default constructor + * Create a new instance of the layer_stack_builder class + * \private + */ + layer_stack_builder(); // can not be created manually +public: //! \publicsection + /*! + * \fn layer_stack_builder* get_instance(); + * \brief Accessor for the unique instance of the logger framework + * \static + */ + static layer_stack_builder* get_instance(); + + /*! + * \fn void register_layer_factory(const std::string & p_type, layer_factory* p_layer_factory); + * \brief Add a new layer factory + * \param[in] p_type The layer identifier (e.g. GN for the GeoNetworking layer...) + * \param[in] p_layer_factory A reference to the \see layer_factory + * \static + */ + static void register_layer_factory(const std::string & p_type, layer_factory* p_layer_factory); + +private: //! \privatesection + /*! + * \fn void _register_layer_factory(const std::string & p_type, layer_factory* p_layer_factory); + * \brief Add a new layer factory + * \param[in] p_type The layer identifier (e.g. GN for the GeoNetworking layer...) + * \param[in] p_layer_factory A reference to the \see layer_factory + */ + void _register_layer_factory(const std::string & p_type, layer_factory* p_layer_factory); + +public: //! \publicsection + /*! + * \fn layer* create_layer_stack(const char* p_layer_stack_description); + * \brief Add a new layer factory + * \param[in] p_layer_stack_description A textual description of the layer to create + * \return The created layer object on success, nullptr otherwise + */ + layer* create_layer_stack(const char* p_layer_stack_description); +}; // End of class layer_stack_builder + diff --git a/ccsrc/Framework/include/loggers.hh b/ccsrc/Framework/include/loggers.hh new file mode 100644 index 0000000..6f252f5 --- /dev/null +++ b/ccsrc/Framework/include/loggers.hh @@ -0,0 +1,289 @@ +/*! + * \file loogers.hh + * \brief Header file for the logger framework. + * \author ETSI STF525 + * \copyright ETSI Copyright Notification + * No part may be reproduced except as authorized by written permission. + * The copyright and the foregoing restriction extend to reproduction in all media. + * All rights reserved. + * \version 0.1 + */ +#pragma once + +#include +#include +#include +#include +#include + +/** +class Base_Type; +class OCTETSTRING; +class TTCN_Buffer; +class TTCN_Logger; +enum TTCN_Logger::Severity; +extern void TTCN_error(const char *err_msg, ...) __attribute__ ((__format__ (__printf__, 1, 2), __noreturn__)); +extern void TTCN_error_begin(const char *err_msg, ...) __attribute__ ((__format__ (__printf__, 1, 2))); +extern void TTCN_error_end() __attribute__ ((__noreturn__)); +void TTCN_warning(const char *warning_msg, ...) __attribute__ ((__format__ (__printf__, 1, 2))); +extern void TTCN_warning_begin(const char *warning_msg, ...) __attribute__ ((__format__ (__printf__, 1, 2))); +extern void TTCN_warning_end(); +**/ + +using namespace std; // Required for isnan() +#include + +/*! + * \class loggers + * \brief This class provides basic functionalities for an ITS dictionary + * \implements Singleton pattern + * \todo Remove reference to TTCN3.hh + */ +class loggers { +private: //! \privatesection + static std::unique_ptr _instance; //! Smart pointer to the unique instance of the logger framework + std::map _times; //! Timer used to measure execution time between calls to methods \loggers::set_start_time and \loggers::set_stop_time + + /*! + * \brief Default constructor + * Create a new instance of the loggers class + * \private + */ + explicit loggers() : _times() { }; + + inline void log_time_exec(const char *p_fmt, ...); + +public: //! \publicsection + /*! + * \brief Default destructor + */ + virtual ~loggers() {}; + + /*! + * \fn static loggers& get_instance(); + * \brief Accessor for the unique instance of the logger framework + * \inline + */ + static inline loggers& get_instance() { return *_instance.get(); }; + + /*! + * \fn void log_to_hexa(const char *p_prompt, const TTCN_Buffer& buffer); + * \brief Hexa dump of the \see TTCN_Buffer buffer + * \param[in] p_prompt Label of the log to be produced + * \param[in] buffer The TTCN_Buffer buffer to dump + * \inline + */ + inline void log_to_hexa(const char *p_prompt, const TTCN_Buffer& buffer); + /*! + * \fn void log_to_hexa(const char *p_prompt, const OCTETSTRING& msg); + * \brief Hexa dump of the \see OCTETSTRING buffer + * \param[in] p_prompt Label of the log to be produced + * \param[in] msg The OCTETSTRING buffer to dump + * \inline + */ + inline void log_to_hexa(const char *p_prompt, const OCTETSTRING& msg); + /*! + * \fn void log_to_hexa(const char *p_prompt, const unsigned char* msg, const size_t msg_size); + * \brief Hexa dump of the provided buffer + * \param[in] p_prompt Label of the log to be produced + * \param[in] msg The buffer to dump + * \inline + */ + inline void log_to_hexa(const char *p_prompt, const unsigned char* msg, const size_t msg_size); + /*! + * \fn void log_msg(const char *p_prompt, const Base_Type& p_type); + * \brief Debug log of TITAN data structures + * \param[in] p_prompt Label of the log to be produced + * \param[in] msg The TITAN data structure to log + * \inline + */ + inline void log_msg(const char *p_prompt, const Base_Type& p_type); + /*! + * \fn void log(const char *p_fmt, ...); + * \brief Debug message based on printf-compliant formatting message + * \param[in] p_fmt The printf-compliant format of the message to log + * \param[in] ... The arguments + * \inline + */ + inline void log(const char *p_fmt, ...); + + /*! + * \fn void user_msg(const char *p_prompt, const Base_Type& p_type); + * \brief User message of TITAN data structures + * \param[in] p_prompt Label of the log to be produced + * \param[in] msg The TITAN data structure to log + * \inline + */ + inline void user_msg(const char *p_prompt, const Base_Type& p_type); + /*! + * \fn void user(const char *p_fmt, ...); + * \brief User message based on printf-compliant formatting message + * \param[in] p_fmt The printf-compliant format of the message to log + * \param[in] ... The arguments + * \inline + */ + inline void user(const char *p_fmt, ...); + + /*! + * \fn void user_msg(const char *p_prompt, const Base_Type& p_type); + * \brief Warning message of TITAN data structures + * \param[in] p_prompt Label of the log to be produced + * \param[in] msg The TITAN data structure to log + * \inline + */ + inline void warning_msg(const char *p_prompt, const Base_Type& p_type); + /*! + * \fn void user(const char *p_fmt, ...); + * \brief Warning message based on printf-compliant formatting message + * \param[in] p_fmt The printf-compliant format of the message to log + * \param[in] ... The arguments + * \inline + */ + inline void warning(const char *p_fmt, ...); + + /*! + * \fn void user(const char *p_fmt, ...); + * \brief Error message based on printf-compliant formatting message + * \param[in] p_fmt The printf-compliant format of the message to log + * \param[in] ... The arguments + * \inline + */ + inline void error(const char *p_fmt, ...); + + /*! + * \fn void set_start_time(std::string& p_time_key); + * \brief Start execution time measurement + * \param[in] p_time_key A timer identifier (any string) + * \inline + */ + inline void set_start_time(std::string& p_time_key); + /*! + * \fn void set_stop_time(std::string& p_time_key, float& p_time); + * \brief Stop execution time measurement + * \param[in] p_time_key The timer identifier provided while calling \see loggers::set_start_time method + * \param[out] p_time The execution time measured in milliseconds + * \inline + */ + inline void set_stop_time(std::string& p_time_key, float& p_time); +}; // End of class loggers + +void loggers::log_to_hexa(const char *p_prompt, const TTCN_Buffer & buffer) +{ + TTCN_Logger::begin_event(TTCN_Logger::DEBUG_UNQUALIFIED); + TTCN_Logger::log_event_str(p_prompt); + buffer.log(); + TTCN_Logger::end_event(); +} + +void loggers::log_to_hexa(const char *p_prompt, const OCTETSTRING& msg) +{ + TTCN_Logger::begin_event(TTCN_Logger::DEBUG_UNQUALIFIED); + TTCN_Logger::log_event_str(p_prompt); + TTCN_Logger::log_event("Size: %d,\nMsg: ", msg.lengthof()); + + for(int i = 0; i < msg.lengthof(); i++) { + TTCN_Logger::log_event(" %02x", ((const unsigned char*)msg)[i]); + } + TTCN_Logger::log_event("\n"); + TTCN_Logger::end_event(); +} + +void loggers::log_to_hexa(const char *p_prompt, const unsigned char* msg, const size_t msg_size) +{ + TTCN_Logger::begin_event(TTCN_Logger::DEBUG_UNQUALIFIED); + TTCN_Logger::log_event_str(p_prompt); + for(size_t i = 0; i < msg_size; i++) { + TTCN_Logger::log_event(" %02x", *(msg + i)); + } + TTCN_Logger::log_event("\n"); + TTCN_Logger::end_event(); +} + +void loggers::log_msg(const char *p_prompt, const Base_Type& p_type) +{ + TTCN_Logger::begin_event(TTCN_Logger::DEBUG_UNQUALIFIED); + TTCN_Logger::log_event_str(p_prompt); + p_type.log(); + TTCN_Logger::end_event(); +} + +void loggers::log(const char *p_fmt, ...) +{ + TTCN_Logger::begin_event(TTCN_Logger::DEBUG_UNQUALIFIED); + va_list args; + va_start(args, p_fmt); + TTCN_Logger::log_event_va_list(p_fmt, args); + va_end(args); + TTCN_Logger::end_event(); +} + +void loggers::user_msg(const char *p_prompt, const Base_Type& p_type) +{ + TTCN_Logger::begin_event(TTCN_Logger::USER_UNQUALIFIED); + TTCN_Logger::log_event_str(p_prompt); + p_type.log(); + TTCN_Logger::end_event(); +} + +void loggers::user(const char *p_fmt, ...) +{ + TTCN_Logger::begin_event(TTCN_Logger::USER_UNQUALIFIED); + va_list args; + va_start(args, p_fmt); + TTCN_Logger::log_event_va_list(p_fmt, args); + va_end(args); + TTCN_Logger::end_event(); +} + +void loggers::warning(const char *p_fmt, ...) +{ + TTCN_Logger::begin_event(TTCN_Logger::WARNING_UNQUALIFIED); + va_list args; + va_start(args, p_fmt); + TTCN_Logger::log_event_va_list(p_fmt, args); + va_end(args); + TTCN_Logger::end_event(); +} + +void loggers::warning_msg(const char *p_prompt, const Base_Type& p_type) +{ + TTCN_Logger::begin_event(TTCN_Logger::WARNING_UNQUALIFIED); + TTCN_Logger::log_event_str(p_prompt); + p_type.log(); + TTCN_Logger::end_event(); +} + +void loggers::error(const char *p_fmt, ...) +{ + va_list args; + va_start(args, p_fmt); + TTCN_error(p_fmt, args); + va_end(args); +} + +void loggers::set_start_time(std::string& p_time_key) +{ + _times[p_time_key] = std::clock(); +} + +void loggers::set_stop_time(std::string& p_time_key, float& p_time) +{ + std::map::iterator it = _times.find(p_time_key); + if (it != loggers::_times.end()) { + p_time = (std::clock() - _times[p_time_key]) * 1000.0 / CLOCKS_PER_SEC; // in milliseconds + _times.erase(it); + loggers::get_instance().log_time_exec("%s: Execution duration: %f ms", p_time_key.c_str(), p_time); + + } +} + +void loggers::log_time_exec(const char *p_fmt, ...) +{ + TTCN_Logger::begin_event(TTCN_Logger::EXECUTOR_RUNTIME); + va_list args; + va_start(args, p_fmt); + TTCN_Logger::log_event_va_list(p_fmt, args); + va_end(args); + TTCN_Logger::end_event(); +} + diff --git a/ccsrc/Framework/include/params.hh b/ccsrc/Framework/include/params.hh new file mode 100644 index 0000000..5ae257b --- /dev/null +++ b/ccsrc/Framework/include/params.hh @@ -0,0 +1,95 @@ +/*! + * \file params.hh + * \brief Header file for the parameter dictionary. + * \author ETSI STF525 + * \copyright ETSI Copyright Notification + * No part may be reproduced except as authorized by written permission. + * The copyright and the foregoing restriction extend to reproduction in all media. + * All rights reserved. + * \version 0.1 + */ +#pragma once + +#include +#include + + /*! + * \class params + * \brief This class provides basic functionalities for an ITS dictionary + * \implements std::map + */ +class params : public std::map { +public: //! \publicsection + // TODO Use static constexpr (see commsignia_layer.hh) + static const std::string& debug; //! Set to 1 to enable the debug mode + + static const std::string& loopback; + + static const std::string& mac_src; //! Source MAC address parameter name + static const std::string& mac_dst; //! Destination MAC address parameter name + static const std::string& mac_bc; //! Broadcast MAC address parameter name + static const std::string& eth_type; //! Ethernet type parameter name + static const std::string& nic; //! Network Interface Card parameter name + static const std::string& latitude; //! Test system Latitude parameter name + static const std::string& longitude; //! Test system Longitude parameter name + static const std::string& device_mode; //! To indicate to the lower layer to act as a standalone device + + static const std::string& server; //! HTTP server address (e.g. www.etsi.org) + static const std::string& port; //! HTTP server port. Default: 80 + static const std::string& use_ssl; //! Set to 1 to use SSL to communicate with the HTTP server. Default: false + static const std::string& server_mode; //! Does the test sytem acting as a server. Default: 0 + static const std::string& local_port; //! Local listener port. Default: 80 + + static const std::string& method; //! HTTP method type. Default: POST + static const std::string& uri; //! HTTP URI value. Default: / + static const std::string& host; //! HTTP Host value. Default: 127.0.0.1 + static const std::string& content_type; //! HTTP Content-type value. Default: application/text + + static const std::string& sip_url; + static const std::string& sip_version; + static const std::string& payload; //! UpperLayer Payload parameter name + + static const std::string& codecs; //! List of codecs to use for HTTP application layers + + /*! + * \brief Default constructor + * Create a new instance of the params class + */ + params() : std::map() {}; + /*! + * \brief Copy constructor + * Clone an existing instance of a params object + * \param[in] p_params An existing instance of a params object + */ + explicit params(const params& p_params) : std::map(p_params.begin(), p_params.end()) { }; + + /*! + * \brief Default destructor + */ + virtual ~params() { }; + + /*! + * \fn void log() const; + * \brief Provides a dump of the content of this instance + */ + void log() const; + /*! + * \fn void log() const; + * \brief Provides a dump of the content of this instance + */ + void log(); + /*! + * \fn void reset(); + * \brief Reset the content of this instance + */ + void reset(); + + /*! + * \static + * \fn void convert(params& p_param, const std::string p_parameters); + * \brief Create a new instance of a params object by converting a list of ITS parameters in string format (t1=v1,T2=(v0,v1v2)...) + * \return a new instance of a params object + */ + static void convert(params& p_param, const std::string p_parameters); +}; // End of class params + diff --git a/ccsrc/Framework/include/t_layer.hh b/ccsrc/Framework/include/t_layer.hh new file mode 100644 index 0000000..8e355a9 --- /dev/null +++ b/ccsrc/Framework/include/t_layer.hh @@ -0,0 +1,72 @@ +/*! + * \file t_layer.hh + * \brief Header file for ITS abstract protocol layer definition. + * \author ETSI STF525 + * \copyright ETSI Copyright Notification + * No part may be reproduced except as authorized by written permission. + * The copyright and the foregoing restriction extend to reproduction in all media. + * All rights reserved. + * \version 0.1 + */ +#pragma once + +#include "layer.hh" + +/*! + * \class t_layer + * \brief This class provides basic description of an ITS port protocol layer. + * A port protocol layer is the final layer which provides the access to the physical communication channel + * A port protocol layer derives from both a layer class and a template port class + */ +template class t_layer : public layer { + typedef std::vector TPortList; + typedef typename std::vector::iterator TPortListIterator; + + TPortList upperPorts; //! The list of the upper ports + +public: //! \publicsection + /*! + * \brief Default constructor + * Create a new instance of the t_layer class + * \todo Remove logs + */ + explicit t_layer() : layer(), upperPorts() { }; + + /*! + * \brief Specialized constructor + * Create a new instance of the layer class with its type description + * \param[in] p_type The port type name (e.g. TCP for the TCP sockect based layer) + * \remark This constructor is called by the layer factory + * \see layer_factory + */ + explicit t_layer(const std::string& p_type) : layer(p_type), upperPorts() { }; + /*! + * \inline + * \fn void add_upper_port(TPort * p_port); + * \brief Add a new upper port layer + * \todo To be done + */ + inline void add_upper_port(TPort * p_port) { upperPorts.push_back(p_port); }; + /*! + * \fn void remove_upper_port(TPort*); + * \brief Remove the specified upper layer port protocol from the list of the upper layers + * \param[in] p_layer The layer protocol to be removed + */ + void remove_upper_port(TPort*); + +protected: //! \protectedsection + /*! + * \inline + * \fn void to_all_upper_ports(const TMessage& m, const params& param); + * \brief Forward the message to all available upper port layers + * \param[in] p_message The message to be forwarded + * \param[in] p_params Some lower layers parameters values when data was received + */ + template + inline void to_all_upper_ports(const TMessage& p_message, const params& p_params) { + for(TPortListIterator it=upperPorts.begin(); itreceiveMsg(p_message, p_params); + } + } +}; // End of class t_layer + diff --git a/ccsrc/Framework/src/base_time.cc b/ccsrc/Framework/src/base_time.cc new file mode 100644 index 0000000..79924f8 --- /dev/null +++ b/ccsrc/Framework/src/base_time.cc @@ -0,0 +1,13 @@ +/*! + * \file base_time.cc + * \brief Source file for the control port base_time functionality. + * \author ETSI STF525 + * \copyright ETSI Copyright Notification + * No part may be reproduced except as authorized by written permission. + * The copyright and the foregoing restriction extend to reproduction in all media. + * All rights reserved. + * \version 0.1 + */ +#include "base_time.hh" + +base_time* base_time::_instance = nullptr; diff --git a/ccsrc/Framework/src/codec_stack_builder.cc b/ccsrc/Framework/src/codec_stack_builder.cc new file mode 100644 index 0000000..2bf087e --- /dev/null +++ b/ccsrc/Framework/src/codec_stack_builder.cc @@ -0,0 +1,4 @@ +#include "codec_stack_builder.hh" + +codec_stack_builder* codec_stack_builder::_instance = NULL; + diff --git a/ccsrc/Framework/src/converter.cc b/ccsrc/Framework/src/converter.cc new file mode 100644 index 0000000..44e9bb8 --- /dev/null +++ b/ccsrc/Framework/src/converter.cc @@ -0,0 +1,202 @@ +#include "converter.hh" +#include +converter * converter::instance = NULL; + +uint16_t converter::swap(const uint16_t p_value) { + uint8_t *ptr = (uint8_t *)&p_value; + return (ptr[0] << 8) | ptr[1]; +} + +uint32_t converter::swap(const uint32_t p_value) { + uint8_t *ptr = (uint8_t *)&p_value; + return (ptr[0] << 24) | (ptr[1] << 16) | (ptr[2] << 8) | ptr[3]; +} + +const std::string converter::lut_u = "0123456789ABCDEF"; +const std::string converter::lut_l = "0123456789abcdef"; +std::string converter::string_to_hexa(const std::string & p_value, const bool p_uppercase) { + + std::string input(p_value); + std::for_each( + input.begin(), + input.end(), + [](char & c) { + c = std::toupper(c); + } + ); + + std::string output; + uint32_t length = p_value.length(); + output.reserve(2 * length); + if (p_uppercase) { // TODO Use pointer to reduce code size + for (uint32_t i = 0; i < length; ++i) { + const uint8_t c = input[i]; + output.push_back(lut_u[c >> 4]); + output.push_back(lut_u[c & 15]); + } // End of 'for' statement + } else { + for (uint32_t i = 0; i < length; ++i) { + const uint8_t c = input[i]; + output.push_back(lut_l[c >> 4]); + output.push_back(lut_l[c & 15]); + } // End of 'for' statement + } + + return output; +} + +std::string converter::bytes_to_hexa(const std::vector & p_value, const bool p_uppercase) { + std::string ret; + ret.assign(p_value.size()*2, ' '); + if (p_uppercase) { // TODO Use pointer to reduce code size + for(size_t i=0; i>4]; + ret[i*2+1] = lut_u[c&0xF]; + } + } else { + for(size_t i=0; i>4]; + ret[i*2+1] = lut_l[c&0xF]; + } + } + return ret; +} + +inline uint8_t char2byte(const char p_ch) { + size_t s = converter::lut_l.find(p_ch); + if(s == std::string::npos) { + if ((s = converter::lut_u.find(p_ch)) == std::string::npos) { + throw (std::length_error("")); + } + } + return s; +} + +std::vector converter::hexa_to_bytes(const std::string & p_value) { + // Sanity check + std::vector output; + size_t i=0, idx = 0, outlen=(p_value.length()+1) / 2; + + output.assign(outlen, 0x00); + try{ + if (p_value.length() & 1) + output[idx++] = char2byte(p_value[i++]); + for(;idx converter::split(const std::string & p_value, const std::string& p_separator) { + std::vector output; + std::size_t current, previous = 0; + current = p_value.find(p_separator); + while (current != std::string::npos) { + output.push_back(p_value.substr(previous, current - previous)); + previous = current + 1; + current = p_value.find(p_separator, previous); + } + output.push_back(p_value.substr(previous, current - previous)); + + return output; +} + +std::vector converter::split_arguments_line(const std::string & p_value) { + std::vector output; + std::string line = trim(p_value); + if (!line.empty() && (line[0] == '-')) { // Valid command line + size_t current = 0; + size_t next = (size_t)-1; + size_t pos = 0; + do { + if (line[pos + 1] == '-') { // -- + current = pos + 2; + } else { + current = pos + 1; + } + next = line.find("-", current); + std::string str(line.substr(pos, next - pos)); + output.push_back(str); + pos = next; + } while (next != std::string::npos); + } // else, invalid command line + return output; +} + +const std::string converter::base64_enc_map = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + +std::vector converter::buffer_to_base64(const std::vector & p_value) { + std::vector out; + + int val = 0, valb = -6; + for (unsigned char c : p_value) { + val = (val << 8) + c; + valb += 8; + while (valb >= 0) { + out.push_back(converter::base64_enc_map[(val >> valb) & 0x3F]); + valb -= 6; + } // End of 'while' statement + } // End of 'for' statement + if (valb > -6) { + out.push_back(converter::base64_enc_map[((val << 8) >> (valb + 8)) & 0x3F]); + } + while (out.size() % 4) { + out.push_back('='); + } // End of 'while' statement + + return out; +} + +std::vector converter::base64_to_buffer(const std::vector &p_value) { + std::vector out; + + std::vector T(256, -1); + for (int i = 0; i < 64; i++) { + T[converter::base64_enc_map[i]] = i; + } + + int val = 0, valb = -8; + for (unsigned char c : p_value) { + if (T[c] == -1) { + break; + } + val = (val << 6) + T[c]; + valb += 6; + if (valb >= 0) { + out.push_back((unsigned char)char((val >> valb) & 0xFF)); + valb -= 8; + } + } // End of 'for' statement + return out; +} diff --git a/ccsrc/Framework/src/layer_factory.cc b/ccsrc/Framework/src/layer_factory.cc new file mode 100644 index 0000000..33f24fe --- /dev/null +++ b/ccsrc/Framework/src/layer_factory.cc @@ -0,0 +1,71 @@ +#include +#include + +#include "layer_stack_builder.hh" + +#include "loggers.hh" + +layer_stack_builder* layer_stack_builder::_instance = NULL; + +// static functions +layer_stack_builder * layer_stack_builder::get_instance() +{ + return _instance ? _instance : _instance = new layer_stack_builder(); +} + +void layer_stack_builder::register_layer_factory(const std::string & p_type, layer_factory* p_layer_factory) +{ + layer_stack_builder::get_instance()->_register_layer_factory(p_type, p_layer_factory); +} + +// member functions +layer_stack_builder::layer_stack_builder() +{ +} + +void layer_stack_builder::_register_layer_factory(const std::string & p_type, layer_factory* p_layer_factory) +{ + _layer_factories[p_type] = p_layer_factory; +} + +layer* layer_stack_builder::create_layer_stack(const char* p_layer_stack_description) +{ + loggers::get_instance().log(">>> layer_stack_builder::create_layer_stack: %s", p_layer_stack_description); + + layer* entry = NULL; // Initial layer (the first declared) + layer* up = NULL; // Upper layer + // Parse the layer description + try { + std::regex rgx ("(\\w+)(\\((.*?)\\))?(\\/|$)"); + std::string str = p_layer_stack_description; + std::sregex_iterator begin(str.cbegin(), str.cend(), rgx); + std::sregex_iterator end = std::sregex_iterator(); + for (std::sregex_iterator it = begin; it != end; ++it) { + std::smatch m = *it; + loggers::get_instance().log("layer_stack_builder::create_layer_stack: %d - %s - %s - %s - %s", m.size(), m[0].str().c_str(), m[1].str().c_str(), m[2].str().c_str(), m[3].str().c_str()); + LayerFactoryMap::iterator i = _layer_factories.find(m[1].str()); + if (i == _layer_factories.end()) { + loggers::get_instance().error("layer_stack_builder::create_layer_stack: %s: Unknown layer type", m[1].str().c_str()); + } + loggers::get_instance().log("layer_stack_builder::create_layer_stack: Create layer %s, %s", m[1].str().c_str(), m[3].str().c_str()); + layer* l = i->second->create_layer(m[1].str(), m[3].str()); + if (NULL == l) { + loggers::get_instance().error("layer_stack_builder::create_layer_stack: %s: Layer creation error", m[1].str().c_str()); + } + + loggers::get_instance().log("layer_stack_builder::create_layer_stack: Setup layers for %s", l->to_string().c_str()); + l->add_upper_layer(up); + if (entry == NULL) { // Set the first declared layer + entry = l; + } + up = l; // Build the linked list of layers + } // End of 'for' statement + } + catch(const std::logic_error& e){ + if(up){ // FIXME To be reviewed + up->delete_layer(); + up = NULL; + } + } + return entry; +} diff --git a/ccsrc/Framework/src/loggers.cc b/ccsrc/Framework/src/loggers.cc new file mode 100644 index 0000000..fba1c59 --- /dev/null +++ b/ccsrc/Framework/src/loggers.cc @@ -0,0 +1,4 @@ +#include "loggers.hh" + +std::unique_ptr loggers::_instance = static_cast>(new loggers); + diff --git a/ccsrc/Framework/src/params.cc b/ccsrc/Framework/src/params.cc new file mode 100644 index 0000000..688ca98 --- /dev/null +++ b/ccsrc/Framework/src/params.cc @@ -0,0 +1,99 @@ +/*! + * \file params.cc + * \brief Source file for the parameter dictionary. + * \author ETSI STF525 + * \copyright ETSI Copyright Notification + * No part may be reproduced except as authorized by written permission. + * The copyright and the foregoing restriction extend to reproduction in all media. + * All rights reserved. + * \version 0.1 + */ +#include +#include + +#include "params.hh" +#include "loggers.hh" + +const std::string& params::debug = std::string("debug"); + +const std::string& params::loopback = std::string("loopback"); + +const std::string& params::mac_src = std::string("mac_src"); +const std::string& params::mac_dst = std::string("mac_dst"); +const std::string& params::mac_bc = std::string("mac_bc"); +const std::string& params::eth_type = std::string("eth_type"); + + +const std::string& params::nic = std::string("nic"); +const std::string& params::latitude = std::string("latitude"); +const std::string& params::longitude = std::string("longitude"); +const std::string& params::device_mode = std::string("device_mode"); + +const std::string& params::server = std::string("server"); +const std::string& params::port = std::string("port"); +const std::string& params::use_ssl = std::string("use_ssl"); +const std::string& params::server_mode = std::string("server_mode"); +const std::string& params::local_port = std::string("local_port"); + +const std::string& params::method = std::string("method"); +const std::string& params::uri = std::string("uri"); +const std::string& params::host = std::string("host"); +const std::string& params::content_type = std::string("content_type"); + +const std::string& params::sip_url = std::string("sip_url"); +const std::string& params::sip_version = std::string("sip_version"); +const std::string& params::payload = std::string("payload"); + +const std::string& params::codecs = std::string("codecs"); + +void params::convert(params& p_param, const std::string p_parameters) { + // Sanity checks + if (p_parameters.length() == 0) { + return; + } + loggers::get_instance().log(">>> params::convert: %s", p_parameters.c_str()); + // Extract parameters + try { + std::regex rgx ("(\\w+)=(.*?)(,|$)"); + std::sregex_iterator begin(p_parameters.cbegin(), p_parameters.cend(), rgx); + std::sregex_iterator end = std::sregex_iterator(); + for (std::sregex_iterator it = begin; it != end; ++it) { + std::smatch m = *it; + loggers::get_instance().log("params::convert: %d - %s - %s - %s - %s", m.size(), m[0].str().c_str(), m[1].str().c_str(), m[2].str().c_str(), m[3].str().c_str()); + p_param.insert(std::pair(m[1].str(), m[2].str())); + } // End of 'for' statement + } + catch(const std::logic_error& e){ + p_param.clear(); + } + loggers::get_instance().log("<<< params::convert"); +} + +void params::log() const { + loggers::get_instance().log("params::log"); + if (size() == 0) { + loggers::get_instance().log("\tEmpty"); + } else { + for (const_iterator it = cbegin(); it != cend(); ++it) { + loggers::get_instance().log("\t(%s, %s)", it->first.c_str(), it->second.c_str()); + } // End of 'for' statement + } +} + +void params::log() { + loggers::get_instance().log("params::log"); + if (size() == 0) { + loggers::get_instance().log("\tEmpty"); + } else { + for (const_iterator it = cbegin(); it != cend(); ++it) { + loggers::get_instance().log("\t(%s, %s)", it->first.c_str(), it->second.c_str()); + } // End of 'for' statement + } +} + +void params::reset() { + loggers::get_instance().log("params::reset"); + for (iterator it = begin(); it != end(); ++it) { + it->second.clear(); + } // End of 'for' statement +} diff --git a/ccsrc/Ports/LibHttp/HttpPort.cc b/ccsrc/Ports/LibHttp/HttpPort.cc new file mode 100644 index 0000000..c20a2af --- /dev/null +++ b/ccsrc/Ports/LibHttp/HttpPort.cc @@ -0,0 +1,113 @@ +#include "HttpPort.hh" +#include "http_layer_factory.hh" +#include "loggers.hh" + +#include "LibItsHttp_TypesAndValues.hh" + +namespace LibItsHttp__TestSystem { + + HttpPort::HttpPort(const char *par_port_name): HttpPort_BASE(par_port_name), _cfg_params(), _layer_params(), _layer(nullptr), _time_key("HttpPort::outgoing_send") { + // Nothing to do + } // End of constructor + + HttpPort::~HttpPort() { + loggers::get_instance().log(">>> HttpPort::~HttpPort"); + + if (_layer != nullptr) { + delete _layer; + } + } // End of destructor + + void HttpPort::set_parameter(const char * parameter_name, const char * parameter_value) + { + loggers::get_instance().log("HttpPort::set_parameter: %s=%s", parameter_name, parameter_value); + _cfg_params.insert(std::pair(std::string(parameter_name), std::string(parameter_value))); + } + + /*void HttpPort::Handle_Fd_Event(int fd, boolean is_readable, + boolean is_writable, boolean is_error) {}*/ + + void HttpPort::Handle_Fd_Event_Error(int /*fd*/) + { + + } + + void HttpPort::Handle_Fd_Event_Writable(int /*fd*/) + { + + } + + void HttpPort::Handle_Fd_Event_Readable(int /*fd*/) + { + + } + + /*void HttpPort::Handle_Timeout(double time_since_last_call) {}*/ + + void HttpPort::user_map(const char * system_port) + { + loggers::get_instance().log(">>> HttpPort::user_map: %s", system_port); + // Build layer stack + params::iterator it = _cfg_params.find(std::string("params")); + if (it != _cfg_params.end()) { + loggers::get_instance().log("HttpPort::user_map: %s", it->second.c_str()); + // Setup parameters + params::convert(_layer_params, it->second); // TODO This _layer_params seems to be useless + // Create layer + _layer = layer_stack_builder::get_instance()->create_layer_stack(it->second.c_str()); + if (static_cast(_layer) == nullptr) { + loggers::get_instance().error("HttpPort::user_map: Invalid stack configuration: %s", it->second.c_str()); + } + static_cast(_layer)->add_upper_port(this); + + } else { + loggers::get_instance().error("HttpPort::user_map: No layers defined in configuration file"); + } + } // End of user_map method + + void HttpPort::user_unmap(const char * system_port) + { + loggers::get_instance().log(">>> HttpPort::user_unmap: %s", system_port); + + // Reset layers + if (_layer != nullptr) { + delete _layer; + _layer = nullptr; + } + } // End of user_unmap method + + void HttpPort::user_start() + { + loggers::get_instance().log(">>> HttpPort::user_start"); + + } // End of user_start method + + void HttpPort::user_stop() + { + loggers::get_instance().log(">>> HttpPort::user_stop"); + + } // End of user_stop method + + void HttpPort::outgoing_send(const LibItsHttp__TypesAndValues::HttpMessage& send_par) + { + loggers::get_instance().log_msg(">>> HttpPort::outgoing_send: payload=", send_par); + + float duration; + loggers::get_instance().set_start_time(_time_key); + params params; + static_cast(_layer)->sendMsg(send_par, params); + loggers::get_instance().set_stop_time(_time_key, duration); + } + + void HttpPort::receiveMsg (const LibItsHttp__TypesAndValues::HttpMessage& p_ind, const params& p_params) { + loggers::get_instance().log_msg(">>> HttpPort::receive_msg: ", p_ind); + // Sanity check + if (!p_ind.is_bound()) { + return; + } + + incoming_message(p_ind); + } + +} + diff --git a/ccsrc/Ports/LibHttp/HttpPort.hh b/ccsrc/Ports/LibHttp/HttpPort.hh new file mode 100644 index 0000000..63c70c5 --- /dev/null +++ b/ccsrc/Ports/LibHttp/HttpPort.hh @@ -0,0 +1,46 @@ +#pragma once + +#include "LibItsHttp_TestSystem.hh" + +#include "layer.hh" +#include "params.hh" + +namespace LibItsHttp__TypesAndValues { + class HttpMessage; +} + +namespace LibItsHttp__TestSystem { + + class HttpPort : public HttpPort_BASE { + params _cfg_params; + params _layer_params; + layer* _layer; + std::string _time_key; + public: + HttpPort(const char *par_port_name); + ~HttpPort(); + + void set_parameter(const char *parameter_name, const char *parameter_value); + void receiveMsg (const LibItsHttp__TypesAndValues::HttpMessage& p_ind, const params& p_params); + + private: + /* void Handle_Fd_Event(int fd, boolean is_readable, + boolean is_writable, boolean is_error); */ + void Handle_Fd_Event_Error(int fd); + void Handle_Fd_Event_Writable(int fd); + void Handle_Fd_Event_Readable(int fd); + /* void Handle_Timeout(double time_since_last_call); */ + + protected: + void user_map(const char *system_port); + void user_unmap(const char *system_port); + + void user_start(); + void user_stop(); + + protected: + void outgoing_send(const LibItsHttp__TypesAndValues::HttpMessage& send_par); + }; // End of class HttpPort + +} + diff --git a/ccsrc/Protocols/ETH/ethernet_layer.cc b/ccsrc/Protocols/ETH/ethernet_layer.cc new file mode 100644 index 0000000..d0ba32a --- /dev/null +++ b/ccsrc/Protocols/ETH/ethernet_layer.cc @@ -0,0 +1,82 @@ +#include "ethernet_layer_factory.hh" + +#include "loggers.hh" + +ethernet_layer::ethernet_layer(const std::string & p_type, const std::string & param) : layer(p_type), _params() { + loggers::get_instance().log(">>> ethernet_layer::ethernet_layer: %s, %s", to_string().c_str(), param.c_str()); + // Setup parameters + params::convert(_params, param); + params::const_iterator it = _params.find("mac_src"); + if (it == _params.cend()) { + _params.insert(std::pair(std::string("mac_src"), "000000000000")); + } + it = _params.find("mac_bc"); + if (it == _params.cend()) { + _params.insert(std::pair(std::string("mac_bc"), "FFFFFFFFFFFF")); + } + it = _params.find("eth_type"); + if (it == _params.cend()) { + _params.insert(std::pair(std::string("eth_type"), "8947")); + } + //_params.log(); +} + +void ethernet_layer::send_data(OCTETSTRING& data, params& params) { + loggers::get_instance().log_msg(">>> ethernet_layer::send_data: ", data); + + OCTETSTRING eth; + // Destination MAC address + params::const_iterator it = params.find(params::mac_dst); // Find in provided parameters, params + if (it != params.cend()) { + eth = str2oct(CHARSTRING(it->second.c_str())); + } else { + it = _params.find(params::mac_dst); + if (it != _params.cend()) { + eth = str2oct(CHARSTRING(it->second.c_str())); + } else { + eth = str2oct(CHARSTRING(_params[params::mac_bc].c_str())); + } + } + // Source MAC address + it = params.find(params::mac_src); // Find in provided parameters, params + if (it != params.cend()) { + eth += str2oct(CHARSTRING(it->second.c_str())); + } else { + eth += str2oct(CHARSTRING(_params[params::mac_src].c_str())); + } + // Ethernet type + it = params.find(params::eth_type); // Find in layer parameters + if (it != params.cend()) { + eth += str2oct(CHARSTRING(it->second.c_str())); + } else { + eth += str2oct(CHARSTRING(_params[params::eth_type].c_str())); + } + + eth += data; + send_to_all_layers(eth, params); +} + +void ethernet_layer::receive_data(OCTETSTRING& data, params& params) { + loggers::get_instance().log_msg(">>> ethernet_layer::receive_data: ", data); + + // Extract dest MAC Address + OCTETSTRING dst = OCTETSTRING(6, static_cast(data)); + //loggers::get_instance().log_msg("ethernet_layer::receive_data: dst: ", dst); + // Extract source MAC Address + OCTETSTRING src = OCTETSTRING(6, 6 + static_cast(data)); + //loggers::get_instance().log_msg("ethernet_layer::receive_data: src: ", src); + // Extract ethertype + OCTETSTRING proto = OCTETSTRING(2, 2 + static_cast(data)); + //loggers::get_instance().log_msg("ethernet_layer::receive_data: proto: ", proto); + data = OCTETSTRING(data.lengthof() - 14, 14 + static_cast(data)); + // Update params + CHARSTRING s = oct2str(dst); + params.insert(std::pair(params::mac_dst, std::string(static_cast(s)))); + s = oct2str(src); + params.insert(std::pair(params::mac_src, std::string(static_cast(s)))); + //loggers::get_instance().log_msg("ethernet_layer::receive_data: payload for upper layer:", data); + + receive_to_all_layers(data, params); +} + +ethernet_layer_factory ethernet_layer_factory::_f; diff --git a/ccsrc/Protocols/ETH/ethernet_layer.hh b/ccsrc/Protocols/ETH/ethernet_layer.hh new file mode 100644 index 0000000..6f224a8 --- /dev/null +++ b/ccsrc/Protocols/ETH/ethernet_layer.hh @@ -0,0 +1,48 @@ +/*! + * \file udp_layer.hh + * \brief Header file for ITS UDP/IP protocol layer definition. + * \author ETSI STF525 + * \copyright ETSI Copyright Notification + * No part may be reproduced except as authorized by written permission. + * The copyright and the foregoing restriction extend to reproduction in all media. + * All rights reserved. + * \version 0.1 + */ +#pragma once + +#include "t_layer.hh" +#include "params.hh" + +class ethernet_layer : public layer { + params _params; //! Layer parameters + +public: //! \publicsection + /*! + * \brief Specialised constructor + * Create a new instance of the ethernet_layer class + * \param[in] p_type \todo + * \param[in] p_param \todo + */ + ethernet_layer(const std::string & p_type, const std::string & param); + /*! + * \brief Default destructor + */ + virtual ~ethernet_layer() {}; + + /*! + * \virtual + * \fn void send_data(OCTETSTRING& data, params& params); + * \brief Send bytes formated data to the lower layers + * \param[in] p_data The data to be sent + * \param[in] p_params Some parameters to overwrite default value of the lower layers parameters + */ + virtual void send_data(OCTETSTRING& data, params& params); + /*! + * \virtual + * \fn void receive_data(OCTETSTRING& data, params& params); + * \brief Receive bytes formated data from the lower layers + * \param[in] p_data The bytes formated data received + * \param[in] p_params Some lower layers parameters values when data was received + */ + virtual void receive_data(OCTETSTRING& data, params& info); +}; // End of class ethernet_layer diff --git a/ccsrc/Protocols/ETH/ethernet_layer_factory.hh b/ccsrc/Protocols/ETH/ethernet_layer_factory.hh new file mode 100644 index 0000000..099a925 --- /dev/null +++ b/ccsrc/Protocols/ETH/ethernet_layer_factory.hh @@ -0,0 +1,45 @@ +/*! + * \file ethernet_layer_factory.hh + * \brief Header file for ITS Ethernet protocol layer factory. + * \author ETSI STF525 + * \copyright ETSI Copyright Notification + * No part may be reproduced except as authorized by written permission. + * The copyright and the foregoing restriction extend to reproduction in all media. + * All rights reserved. + * \version 0.1 + */ +#pragma once + +#include "layer_stack_builder.hh" + +#include "ethernet_layer.hh" + +/*! + * \class ethernet_layer_factory + * \brief This class provides a factory class to create an ethernet_layer class instance + */ +class ethernet_layer_factory: public layer_factory { + static ethernet_layer_factory _f; //! Reference to the unique instance of this class +public: //! \publicsection + /*! + * \brief Default constructor + * Create a new instance of the ethernet_layer_factory class + * \remark The ETH layer identifier is ETH + */ + ethernet_layer_factory() { + // register factory + layer_stack_builder::register_layer_factory("ETH", this); + }; + /*! + * \fn layer* create_layer(const std::string & type, const std::string & param); + * \brief Create the layers stack based on the provided layers stack description + * \param[in] p_type The provided layers stack description + * \param[in] p_params Optional parameters + * \return 0 on success, -1 otherwise + * \inline + */ + inline virtual layer* create_layer(const std::string& p_type, const std::string& p_param) { + return new ethernet_layer(p_type, p_param); + }; +}; // End of class ethernet_layer_factory + diff --git a/ccsrc/Protocols/Http/http_codec.cc b/ccsrc/Protocols/Http/http_codec.cc new file mode 100644 index 0000000..3c560cc --- /dev/null +++ b/ccsrc/Protocols/Http/http_codec.cc @@ -0,0 +1,766 @@ +#include +#include +#include + +#include "codec_stack_builder.hh" + +#include "http_codec.hh" + +#include "loggers.hh" + +#include "converter.hh" + +#include "LibItsHttp_TypesAndValues.hh" +#include "LibItsHttp_MessageBodyTypes.hh" +#include "LibItsHttp_XmlMessageBodyTypes.hh" + +int http_codec::encode (const LibItsHttp__TypesAndValues::HttpMessage& msg, OCTETSTRING& data) +{ + loggers::get_instance().log_msg(">>> http_codec::encode: ", (const Base_Type&)msg); + loggers::get_instance().log(">>> http_codec::encode: %p", this); + + TTCN_EncDec::clear_error(); + TTCN_Buffer encoding_buffer; + + _ec.reset(); + + int result; + if (msg.ischosen(LibItsHttp__TypesAndValues::HttpMessage::ALT_request)) { + result = encode_request(msg.request(), encoding_buffer); + } else if (msg.ischosen(LibItsHttp__TypesAndValues::HttpMessage::ALT_response)) { + result = encode_response(msg.response(), encoding_buffer); + } else { + loggers::get_instance().warning("http_codec::encode: Unbound HttpMessage"); + return -1; + } + + data = OCTETSTRING(encoding_buffer.get_len(), encoding_buffer.get_data()); + + loggers::get_instance().log_msg("<<< http_codec::encode: data=", data); + return result; +} + +int http_codec::decode (const OCTETSTRING& data, LibItsHttp__TypesAndValues::HttpMessage& msg, params* params) +{ + loggers::get_instance().log_msg(">>> http_codec::decode: data=", data); + + TTCN_EncDec::clear_error(); + TTCN_Buffer decoding_buffer(data); + loggers::get_instance().log_to_hexa("http_codec::decode: decoding_buffer=", decoding_buffer); + + _dc.reset(); + + _params = params; + + // Get the first line (e.g. HTTP/1.1 302 Found or POST / HTTP/1.1) + CHARSTRING message_id; + if (get_line(decoding_buffer, message_id) == -1) { + return -1; + } + loggers::get_instance().log_msg("http_codec::decode: message_id: ", message_id); + // Extract parameters + try { + std::string str(static_cast(message_id)); + std::regex rgx ("\\s*(\\w+)/"); + std::sregex_iterator begin(str.cbegin(), str.cend(), rgx); + std::smatch m = *begin; + loggers::get_instance().log("http_codec::decode: %d - %s", m.size(), m[0].str().c_str()); + if (m[0].str().compare("HTTP/") == 0) { // HTTP response + LibItsHttp__TypesAndValues::Response response; + std::regex rgx ("\\s*HTTP/(\\d+)\\.(\\d+)\\s+(\\d+)\\s+([\\w\\s\\t\\v\\f]+)*"); + std::sregex_iterator begin(str.cbegin(), str.cend(), rgx); + std::smatch m = *begin; + loggers::get_instance().log("http_codec::decode: Process response: %d", m.size()); + if (m.size() != 5) { + loggers::get_instance().error("http_codec::decode: Unsupported tag"); + return -1; + } + response.version__major() = std::stoi(m[1].str().c_str()); + response.version__minor() = std::stoi(m[2].str().c_str()); + response.statuscode() = std::stoi(m[3].str().c_str()); + response.statustext() = CHARSTRING(m[4].str().c_str()); + LibItsHttp__TypesAndValues::HeaderLines headers; + std::string content_type; + decode_headers(decoding_buffer, headers, content_type); + response.header() = headers; + loggers::get_instance().log_to_hexa("Before decoding Body: ", decoding_buffer); + LibItsHttp__MessageBodyTypes::HttpMessageBody body; + if (decode_body(decoding_buffer, body, content_type) == -1) { + response.body().set_to_omit(); + } else { + response.body() = OPTIONAL(body); + } + msg.response() = response; + } else { // HTTP request + LibItsHttp__TypesAndValues::Request request; + std::regex rgx ("\\s*(\\w+)\\s+(.+)\\s+HTTP/(\\d)\\.(\\d)"); + std::sregex_iterator begin(str.cbegin(), str.cend(), rgx); + std::smatch m = *begin; + if (m.size() != 5) { + loggers::get_instance().error("http_codec::decode: Unsupported tag"); + return -1; + } + request.method() = CHARSTRING(m[1].str().c_str()); + request.uri() = CHARSTRING(m[2].str().c_str()); + request.version__major() = std::stoi(m[3].str().c_str()); + request.version__minor() = std::stoi(m[4].str().c_str()); + LibItsHttp__TypesAndValues::HeaderLines headers; + std::string content_type; + decode_headers(decoding_buffer, headers, content_type); + request.header() = headers; + OPTIONAL body; + body.set_to_omit(); + if (decode_body(decoding_buffer, body, content_type) == -1) { + request.body().set_to_omit(); + } else { + request.body() = body; + } + msg.request() = request; + } + + loggers::get_instance().log_msg("<<< http_codec::decode: ", (const Base_Type&)msg); + return 0; + } + catch(const std::logic_error& e) { + return -1; + } +} + +int http_codec::encode_request(const LibItsHttp__TypesAndValues::Request& p_request, TTCN_Buffer& p_encoding_buffer) +{ + loggers::get_instance().log_msg(">>> http_codec::encode_request: ", (const Base_Type&)p_request); + + // Encode generic part + p_encoding_buffer.put_cs(p_request.method()); + p_encoding_buffer.put_c(' '); + p_encoding_buffer.put_cs(p_request.uri()); + p_encoding_buffer.put_cs(" HTTP/"); + p_encoding_buffer.put_cs(int2str(p_request.version__major())); + p_encoding_buffer.put_c('.'); + p_encoding_buffer.put_cs(int2str(p_request.version__minor())); + p_encoding_buffer.put_cs("\r\n"); + + // Encode headers excepeted the Content-Length + const LibItsHttp__TypesAndValues::HeaderLines& headers = p_request.header(); + std::string content_type; + for (int i = 0; i < headers.size_of(); i++) { + const LibItsHttp__TypesAndValues::HeaderLine& header = headers[i]; + loggers::get_instance().log_msg("http_codec::encode_request: Processing header ", header.header__name()); + if (std::string(static_cast(header.header__name())).compare("Content-Length") == 0) { // Skip it, processed later + loggers::get_instance().log("http_codec::encode_request: Skip it"); + continue; + } else { + p_encoding_buffer.put_cs(header.header__name()); + p_encoding_buffer.put_cs(": "); + const OPTIONAL& o = header.header__value(); + if (o.ispresent()) { + const LibItsHttp__TypesAndValues::charstring__list& v = dynamic_cast &>(o); + if (v.size_of() > 0) { + loggers::get_instance().log_msg("http_codec::encode_request: Processing value ", v[0]); + if (std::string(static_cast(header.header__name())).compare("Content-Type") == 0) { // Store it for HTTP body payload encoding + loggers::get_instance().log("http_codec::encode_request: Storing Content-Type"); + int j = 0; + while (j < v.size_of()) { + content_type += v[j++]; + } // End of 'while' statement + } + p_encoding_buffer.put_cs(v[0]); + int j = 1; + while (j < v.size_of()) { + p_encoding_buffer.put_cs(", "); + loggers::get_instance().log_msg("http_codec::encode_request: Processing value ", v[j]); + p_encoding_buffer.put_cs(v[j++]); + } // End of 'while' statement + } + } // else, do not include it + } + p_encoding_buffer.put_cs("\r\n"); + } // End of 'for' statement + + // Encode message body + const OPTIONAL& v = p_request.body(); + OCTETSTRING os; + if (v.ispresent()) { + const LibItsHttp__MessageBodyTypes::HttpMessageBody& body = static_cast(*v.get_opt_value()); + loggers::get_instance().log_msg("http_codec::encode_request: body: ", body); + if (encode_body(body, os, content_type) == -1) { + loggers::get_instance().warning("http_codec::encode_request: Failed to encode HTTP body"); + _ec.length = 0; + _ec.is_content_length_present = 0x00; + } else { + _ec.length = os.lengthof(); + _ec.is_content_length_present = 0x01; + } + loggers::get_instance().log("http_codec::encode_request: length=%d", _ec.length); + } else { + loggers::get_instance().log("http_codec::encode_request: HTTP body field not present"); + _ec.length = 0; + _ec.is_content_length_present = 0x00; + } + + // Encode Content-Length header + p_encoding_buffer.put_cs("Content-Length: "); + if (_ec.length != 0) { + loggers::get_instance().log("http_codec::encode_request: Content-Length: %s", static_cast(int2str(_ec.length + 2/*Stand for the last CRLF*/))); + p_encoding_buffer.put_cs(static_cast(int2str(_ec.length + 2/*Stand for the last CRLF*/))); + _ec.is_content_length_present = 0x01; + } else { + p_encoding_buffer.put_cs("0"); + _ec.is_content_length_present = 0x00; + } + p_encoding_buffer.put_cs("\r\n"); + + // Add message body + p_encoding_buffer.put_cs("\r\n"); + if (_ec.is_content_length_present == 0x01) { + loggers::get_instance().log_msg("http_codec::encode_request: Add body ", os); + p_encoding_buffer.put_os(os); + p_encoding_buffer.put_cs("\r\n"); + } + + loggers::get_instance().log_to_hexa("<<< http_codec::encode_request: ", p_encoding_buffer); + return 0; +} + +int http_codec::encode_response (const LibItsHttp__TypesAndValues::Response& p_response, TTCN_Buffer& p_encoding_buffer) { + loggers::get_instance().log_msg(">>> http_codec::encode_response: ", (const Base_Type&)p_response); + + // Encode generic part + p_encoding_buffer.put_cs("HTTP/"); + p_encoding_buffer.put_cs(int2str(p_response.version__major())); + p_encoding_buffer.put_c('.'); + p_encoding_buffer.put_cs(int2str(p_response.version__minor())); + p_encoding_buffer.put_cs(" "); + p_encoding_buffer.put_cs(int2str(p_response.statuscode())); + p_encoding_buffer.put_cs(" "); + if (p_response.statustext().lengthof() != 0) { + p_encoding_buffer.put_cs(p_response.statustext()); + } + p_encoding_buffer.put_cs("\r\n"); + + // Encode headers excepeted the Content-Length + const LibItsHttp__TypesAndValues::HeaderLines& headers = p_response.header(); + std::string content_type; + for (int i = 0; i < headers.size_of(); i++) { + const LibItsHttp__TypesAndValues::HeaderLine& header = headers[i]; + loggers::get_instance().log_msg("http_codec::encode_response: Processing header ", header.header__name()); + p_encoding_buffer.put_cs(header.header__name()); + p_encoding_buffer.put_cs(": "); + if (std::string(static_cast(header.header__name())).compare("Content-Length") == 0) { + continue; + } else { + const OPTIONAL& o = header.header__value(); + if (o.ispresent()) { + const LibItsHttp__TypesAndValues::charstring__list& v = dynamic_cast &>(o); + if (v.size_of() > 0) { + loggers::get_instance().log_msg("http_codec::encode_response: Processing value ", v[0]); + if (std::string(static_cast(header.header__name())).compare("Content-Type") == 0) { // Store it for HTTP body payload encoding + int j = 1; + while (j < v.size_of()) { + content_type += v[j++]; + } // End of 'while' statement + } + p_encoding_buffer.put_cs(v[0]); + int j = 1; + while (j < v.size_of()) { + p_encoding_buffer.put_cs(", "); + loggers::get_instance().log_msg("http_codec::encode_response: Processing value ", v[j]); + p_encoding_buffer.put_cs(v[j]); + j += 1; + } // End of 'while' statement + } + } // else, do not include it + } + p_encoding_buffer.put_cs("\r\n"); + } // End of 'for' statement + + // Encode message body + const OPTIONAL& v = p_response.body(); + OCTETSTRING os; + if (v.ispresent()) { + const LibItsHttp__MessageBodyTypes::HttpMessageBody& body = static_cast(*v.get_opt_value()); + loggers::get_instance().log_msg("http_codec::encode_response: body: ", body); + if (encode_body(body, os, content_type) == -1) { + _ec.length = 0; + _ec.is_content_length_present = 0x00; + } else { + _ec.length = os.lengthof(); + _ec.is_content_length_present = 0x01; + } + loggers::get_instance().log("http_codec::encode_request: length=%d", _ec.length); + } else { + loggers::get_instance().log("http_codec::encode_request: HTTP body field not present"); + _ec.length = 0; + _ec.is_content_length_present = 0x00; + } + + // Encode Content-Length header + if (_ec.length != 0) { + p_encoding_buffer.put_cs(int2str(_ec.length + 2/*Stand for the last CRLF*/)); + _ec.is_content_length_present = 0x01; + } else { + p_encoding_buffer.put_cs("0"); + _ec.is_content_length_present = 0x00; + } + loggers::get_instance().log("http_codec::encode_response: Content-Length: %d - %x", _ec.length, _ec.is_content_length_present); + p_encoding_buffer.put_cs("\r\n"); + + p_encoding_buffer.put_cs("\r\n"); + if (_ec.length != 0) { + p_encoding_buffer.put_os(os); + p_encoding_buffer.put_cs("\r\n"); + } + + return 0; +} + +int http_codec::decode_headers(TTCN_Buffer& decoding_buffer, LibItsHttp__TypesAndValues::HeaderLines& headers, std::string& p_content_type) { + loggers::get_instance().log(">>> http_codec::decode_headers"); + loggers::get_instance().log_to_hexa("http_codec::decode_headers", decoding_buffer); + + CHARSTRING cstr; + int i = 0; + while (true) { + switch(get_line(decoding_buffer, cstr, true)) { + case 0: { + loggers::get_instance().log_msg("http_codec::decode_headers: ", cstr); + LibItsHttp__TypesAndValues::HeaderLine header; + if (decode_header(cstr, header) == -1) { + loggers::get_instance().warning("http_codec::decode_headers: Failed to decode header %s", static_cast(cstr)); + return -1; + } + headers[i++] = header; + if (std::string(static_cast(header.header__name())).compare("Content-Type") == 0) { + if (header.header__value().is_present() != 0) { + const PreGenRecordOf::PREGEN__RECORD__OF__CHARSTRING& l = static_cast(*header.header__value().get_opt_value()); + p_content_type = static_cast(l[0]); + } else { + p_content_type = ""; + } + } + } + break; + case 1: + loggers::get_instance().log_msg("<<< http_codec::decode_headers: ", headers); + return 0; + case -1: + loggers::get_instance().warning("http_codec::decode_headers: Failed to decode headers"); + return -1; + } // End of 'switch' statement + } // End of 'while' statement +} + +int http_codec::decode_header(CHARSTRING& header_line, LibItsHttp__TypesAndValues::HeaderLine& header) { + loggers::get_instance().log_msg(">>> http_codec::decode_header", header_line); + + try { + std::string str(static_cast(header_line)); + std::regex rgx ("([0-9a-zA-Z-]+)\\:\\s+(.+)(,(.+))*"); + std::sregex_iterator begin(str.cbegin(), str.cend(), rgx); + std::smatch m = *begin; + if (m.size() < 5) { + loggers::get_instance().warning("http_codec::decode_header: Failed to decode header %s", str.c_str()); + return -1; + } + loggers::get_instance().log("http_codec::decode_header: %d", m.size()); + header.header__name() = CHARSTRING(m[1].str().c_str()); + LibItsHttp__TypesAndValues::charstring__list v; + for (unsigned int j = 0; j < m.size(); j++) { + if (m[j + 2].str().length() == 0) { + break; + } + v[j] = CHARSTRING(m[j + 2].str().c_str()); + } // End of 'for' statement + header.header__value() = OPTIONAL(v); + + if (m[1].str().compare("Content-Length") == 0) { + // Save the the body length + loggers::get_instance().log("http_codec::decode_header: decoded Content-Length %s", m[2].str().c_str()); + _dc.length = std::stoi(m[2].str()); + } else if (m[1].str().compare("Transfer-Encoding") == 0) { + if (m[2].str().find("chunked") != std::string::npos) { + _dc.chunked = true; + loggers::get_instance().log("http_codec::decode_header: decoded Transfer-Encoding %x", _dc.chunked); + } + } + + return 0; + } + catch(const std::logic_error& e) { + return -1; + } +} + +int http_codec::encode_body(const LibItsHttp__MessageBodyTypes::HttpMessageBody& p_message_body, OCTETSTRING& p_encoding_buffer, const std::string& p_content_type) { + loggers::get_instance().log_msg(">>> http_codec::encode_body: ", (const Base_Type&)p_message_body); + + // Sanity check + if (p_content_type.empty()) { + loggers::get_instance().warning("http_codec::encode_body: Failed to select a codec for HTTP body payload"); + return -1; + } + + if (p_message_body.ischosen(LibItsHttp__MessageBodyTypes::HttpMessageBody::ALT_binary__body)) { + const LibItsHttp__BinaryMessageBodyTypes::BinaryBody& binary_body = p_message_body.binary__body(); + if (binary_body.ischosen(LibItsHttp__BinaryMessageBodyTypes::BinaryBody::ALT_raw)) { + p_encoding_buffer = OCTETSTRING(binary_body.raw().lengthof(), (unsigned char*)static_cast(binary_body.raw())); + } else { + std::map > >::const_iterator it; + bool processed = false; + if (p_content_type.find("x-its") != std::string::npos) { + loggers::get_instance().log("http_codec::encode_body: Find x-its"); + it = _codecs.find("http_its"); // TODO Use params + if (it != _codecs.cend()) { + loggers::get_instance().log("http_codec::encode_body: Call '%s'", it->first.c_str()); + //_codecs["http_its"]->encode((Record_Type&)binary_body.ieee1609dot2__data(), p_encoding_buffer); // TODO Use params + + +#if defined(GEMALTO_FIX) // Temporary fix to be removed + // GEMALTO Encode in hex string + CHARSTRING buf = oct2str(p_encoding_buffer); + p_encoding_buffer = OCTETSTRING(buf.lengthof(), (const unsigned char*)(static_cast(buf))); + loggers::get_instance().log_msg("http_codec::encode_body: Convert binary to string: ", p_encoding_buffer); +#endif + + + processed = true; + } + } // TODO Add new HTTP message codec here + if (!processed) { + loggers::get_instance().warning("http_codec::encode_body: Unsupported HTTP codec, use raw field as default"); + p_encoding_buffer = OCTETSTRING(0, nullptr); + } + } + } else if (p_message_body.ischosen(LibItsHttp__MessageBodyTypes::HttpMessageBody::ALT_html__body)) { + p_encoding_buffer = OCTETSTRING(p_message_body.html__body().lengthof(), (unsigned char*)static_cast(p_message_body.html__body())); + } else if (p_message_body.ischosen(LibItsHttp__MessageBodyTypes::HttpMessageBody::ALT_text__body)) { + p_encoding_buffer = OCTETSTRING(p_message_body.text__body().lengthof(), (unsigned char*)static_cast(p_message_body.text__body())); + } else if (p_message_body.ischosen(LibItsHttp__MessageBodyTypes::HttpMessageBody::ALT_xml__body)) { + const LibItsHttp__XmlMessageBodyTypes::XmlBody& xml_body = p_message_body.xml__body(); + if (xml_body.ischosen(LibItsHttp__XmlMessageBodyTypes::XmlBody::ALT_raw)) { + p_encoding_buffer = OCTETSTRING(xml_body.raw().lengthof(), (unsigned char*)static_cast(xml_body.raw())); + } else { + std::map > >::const_iterator it; + bool processed = false; + loggers::get_instance().log("http_codec::encode_body: Content-Type:'%s'", p_content_type.c_str()); + if (p_content_type.find("held") != std::string::npos) { + it = _codecs.find("held"); // TODO Use params + if (it != _codecs.cend()) { + loggers::get_instance().log("http_codec::encode_body: Call 'held_codec'"); + _codecs["held"]->encode((Record_Type&)xml_body, p_encoding_buffer); // TODO Use params + processed = true; + } + } else if (p_content_type.find("lost") != std::string::npos) { + it = _codecs.find("lost"); // TODO Use params + if (it != _codecs.cend()) { + loggers::get_instance().log("http_codec::encode_body: Call 'lost_codec'"); + _codecs["lost"]->encode((Record_Type&)xml_body, p_encoding_buffer); // TODO Use params + processed = true; + } + } // TODO Add new HTTP message codec here + if (!processed) { + loggers::get_instance().warning("http_codec::encode_body: Unsupported HTTP codec, use raw field as default"); + p_encoding_buffer = OCTETSTRING(0, nullptr); + } + } + } else { + loggers::get_instance().warning("http_codec::encode_body: Failed to encode HTTP message body"); + return -1; + } + loggers::get_instance().log_msg("http_codec::encode_body: HTTP message ", p_encoding_buffer); + _ec.length = p_encoding_buffer.lengthof(); + loggers::get_instance().log("http_codec::encode_body: HTTP message length: %d", _ec.length); + + return 0; +} + +int http_codec::decode_body(TTCN_Buffer& decoding_buffer, LibItsHttp__MessageBodyTypes::HttpMessageBody& message_body, const std::string& p_content_type) { + loggers::get_instance().log(">>> http_codec::decode_body"); + loggers::get_instance().log_to_hexa("http_codec::decode_body", decoding_buffer); + loggers::get_instance().log("http_codec::decode_body: # of codecs=%d - %p", _codecs.size(), this); + loggers::get_instance().log("http_codec::decode_body: Content-Type=%s", p_content_type.c_str()); + + // Sanity checks + if (decoding_buffer.get_len() - decoding_buffer.get_pos() <= 0) { + return -1; + } + /* TODO Uncommentif (p_content_type.empty()) { + loggers::get_instance().warning("http_codec::encode_body: Failed to select a codec for HTTP body payload"); + return -1; + }*/ + + OCTETSTRING s(decoding_buffer.get_len() - decoding_buffer.get_pos(), decoding_buffer.get_data() + decoding_buffer.get_pos()); + loggers::get_instance().log_msg("http_codec::decode_body: raw body=", s); + +#if defined(GEMALTO_FIX) // Temporary fix to be removed + // GEMALTO Encode in hex string + if ((s.lengthof() & 0x00000001) == 0x00000001) { + s = int2oct(0, 1) + s; + } + s = str2oct(CHARSTRING(s.lengthof(), (const char*)(static_cast(s)))); + loggers::get_instance().log_msg("http_codec::decode_body: Convert string to binary: ", s); +#endif + + // Align the payload length with the specified Content-Lenght value + loggers::get_instance().log("http_codec::decode_body: _dc.length=%d - body length=%d", _dc.length, s.lengthof()); + OCTETSTRING body; + if (_dc.length != 0) { + const unsigned char* p = static_cast(s); + if ((unsigned int)s.lengthof() <= _dc.length) { + body = OCTETSTRING(s.lengthof(), p); + } else { + body = OCTETSTRING(_dc.length, p); + } + } else { + loggers::get_instance().warning("http_codec::decode_body: No Conten-Length header, process all remaining bytes"); + body = s; + } + loggers::get_instance().log_msg("http_codec::decode_body: Aligned body=", body); + // Remove CRLF if any + int counter = 0; + if ((body[body.lengthof() - 1].get_octet() == 0x0d) || (body[body.lengthof() - 1].get_octet() == 0x0a)) { + counter += 1; + if ((body[body.lengthof() - 2].get_octet() == 0x0d) || (body[body.lengthof() - 2].get_octet() == 0x0a)) { + counter += 1; + } + } + loggers::get_instance().log("http_codec::decode_body: counter=%d", counter); + loggers::get_instance().log("http_codec::decode_body: body length=%d", body.lengthof()); + body = OCTETSTRING(body.lengthof() - counter, static_cast(body)); + if (_dc.chunked) { + counter = 0; + int prev = 0; + OCTETSTRING os(0, nullptr); + do { + while (counter < body.lengthof()) { // Extract the size of the chunk \r[\n] + if ((body[counter].get_octet() == '\r') || (body[counter].get_octet() == '\n')) { + break; + } + counter += 1; + } // End of 'while' statement + loggers::get_instance().log("http_codec::decode_body: Chunked(0): prev = %d, counter=%d / %d", prev, counter, body.lengthof()); + if (counter < body.lengthof()) { + int idx = counter - prev; + OCTETSTRING trunk(idx, static_cast(body)); + loggers::get_instance().log_msg("http_codec::decode_body: trunk: ", trunk); + std::string str((const char*)static_cast(trunk), idx); + loggers::get_instance().log("http_codec::decode_body: str: '%s'", str.c_str()); + int len = std::stoi(str, nullptr, 16);//converter::get_instance().string_to_int(str); + loggers::get_instance().log("http_codec::decode_body: Chunk len: %d", len); + while (counter < body.lengthof() && ((body[counter].get_octet() == '\r') || (body[counter].get_octet() == '\n'))) { // Skip additional \n + counter += 1; + } // End of 'while' statement + if (counter < body.lengthof()) { + loggers::get_instance().log("http_codec::decode_body: Chunked (1): prev = %d, counter=%d / %d", prev, counter, body.lengthof()); + os += OCTETSTRING(len, counter + static_cast(body)); + loggers::get_instance().log_msg("http_codec::decode_body: os=", os); + counter += len; + loggers::get_instance().log("http_codec::decode_body: Chunked: %02x %02x %02x", body[counter].get_octet(), body[counter + 1].get_octet(), body[counter + 2].get_octet()); + loggers::get_instance().log("http_codec::decode_body: Chunked (2): prev = %d, counter=%d / %d", prev, counter, body.lengthof()); + while (counter < body.lengthof() && ((body[counter].get_octet() == '\r') || (body[counter].get_octet() == '\n'))) { // Skip additional \n + counter += 1; + } // End of 'while' statement + prev = counter; + loggers::get_instance().log("http_codec::decode_body: Chunked (3): prev = %d, counter=%d / %d", prev, counter, body.lengthof()); + } + } + } while (counter < body.lengthof()); // Process next chunk if any + body = os; + } + loggers::get_instance().log_msg("http_codec::decode_body: Finalised body=", body); + // Check if HTTP message body contains binary characters + for (int i = 0; i < body.lengthof(); i++) { + unsigned char c = body[i].get_octet(); + if (!std::isprint(c) && !std::isspace(c) && !std::ispunct(c)) { + loggers::get_instance().log("http_codec::decode_body: Byte #%d is not printable: 0x%02x", i, body[i].get_octet()); + _dc.is_binary = 0x01; + break; + } + } // End of 'for' statement + loggers::get_instance().log("http_codec::decode_body: Binary mode: %x", _dc.is_binary); + LibItsHttp__MessageBodyTypes::HttpMessageBody v; + if (_dc.is_binary == 0x01) { + LibItsHttp__BinaryMessageBodyTypes::BinaryBody binary_body; + std::map > >::const_iterator it; + bool processed = false; + // TODO To be refined adding a string identifier to check which codec to use. E.g. held_code.id() returns "xmlns=\"urn:ietf:params:xml:ns:geopriv:held\">" + if (p_content_type.find("x-its") != std::string::npos) { + loggers::get_instance().log("http_codec::decode_body: Find 'x-its'"); + it = _codecs.cbegin();//_codecs.find("http_its"); + if (it != _codecs.cend()) { + /*** + FIXME: + This code generate a codedump, I don't undertsand the reason. + The same code works file for Ng112 HELD & LOST codec. Ununderstandable!!!! + ==> Use a patch + if (_codecs["http_its"].get() != nullptr) { + loggers::get_instance().log("http_codec::decode_body: Call 'http_etsi_ieee1609dot2_codec'"); + if (_codecs["http_its"]->decode(body, (Record_Type&)binary_body) == 0) { + processed = true; + } + }*/ + loggers::get_instance().log("http_codec::decode_body: Call '%s'", it->first.c_str()); + /*http_etsi_ieee1609dot2_codec* codec = new http_etsi_ieee1609dot2_codec(); + if (codec->decode(body, binary_body.ieee1609dot2__data()) == 0) { + message_body.binary__body() = binary_body; + processed = true; + } + delete codec;*/ + } + } // TODO Add new HTTP message codec here + if (!processed) { + loggers::get_instance().warning("http_codec::decode_body: Unsupported HTTP codec, use raw field as default"); + binary_body.raw() = body; + message_body.binary__body() = binary_body; + } + } else { + // Convert into string + params p; + p["decode_str"] = std::string(static_cast(body), body.lengthof() + static_cast(body)); + loggers::get_instance().log("http_codec::decode_body: decode_str: %s", p["decode_str"].c_str()); + // Try to identify xml + if (p["decode_str"].find("" + if ( + (p["decode_str"].find("=\"urn:ietf:params:xml:ns:geopriv:held\"") != std::string::npos) || + (p["decode_str"].find("=\"urn:ietf:params:xml:ns:pidf\"") != std::string::npos) + ) { + loggers::get_instance().log("http_codec::decode_body: Find 'urn:ietf:params:xml:ns:geopriv:held'"); + if (_codecs["held"].get() != nullptr) { + loggers::get_instance().log("http_codec::decode_body: Call 'held_codec'"); + if (_codecs["held"]->decode(body, (Record_Type&)xml_body, &p) == -1) { + loggers::get_instance().warning("http_codec::decode_body: Failed to decode HELD message"); + xml_body.raw() = CHARSTRING(body.lengthof(), (char*)static_cast(body)); + } else { + loggers::get_instance().log_msg("http_codec::decode_body: Decoded message:", xml_body); + message_body.xml__body() = xml_body; + } + } else { + loggers::get_instance().warning("http_codec::decode_body: No codec for HELD"); + xml_body.raw() = CHARSTRING(body.lengthof(), (char*)static_cast(body)); + } + message_body.xml__body() = xml_body; + } else if (p["decode_str"].find("=\"urn:ietf:params:xml:ns:lost1\"") != std::string::npos) { + loggers::get_instance().log("http_codec::decode_body: Find 'urn:ietf:params:xml:ns:lost1'"); + if (_codecs["lost"].get() != nullptr) { + loggers::get_instance().log("http_codec::decode_body: Call 'lost_codec'"); + if (_codecs["lost"]->decode(body, (Record_Type&)xml_body, &p) == -1) { + loggers::get_instance().warning("http_codec::decode_body: Failed to decode LOST message"); + xml_body.raw() = CHARSTRING(body.lengthof(), (char*)static_cast(body)); + } else { + loggers::get_instance().log_msg("http_codec::decode_body: Decoded message:", xml_body); + message_body.xml__body() = xml_body; + } + } else { + loggers::get_instance().warning("http_codec::decode_body: No codec for LOST"); + xml_body.raw() = CHARSTRING(body.lengthof(), (char*)static_cast(body)); + } + message_body.xml__body() = xml_body; + } else { + loggers::get_instance().warning("http_codec::decode_body: No XML codec found"); + xml_body.raw() = CHARSTRING(body.lengthof(), (char*)static_cast(body)); + message_body.xml__body() = xml_body; + } + } else if (p_content_type.find("json") != std::string::npos) { // Try to identify JSON + loggers::get_instance().log("http_codec::decode_body: Find json message"); + LibItsHttp__JsonMessageBodyTypes::JsonBody json_body; + if (_codecs["json"].get() != nullptr) { + loggers::get_instance().log("http_codec::decode_body: Call 'json_codec'"); + if (_codecs["json"]->decode(body, (Record_Type&)json_body, &p) == -1) { + loggers::get_instance().warning("http_codec::decode_body: Failed to decode JSON message"); + json_body.raw() = CHARSTRING(body.lengthof(), (char*)static_cast(body)); + } else { + loggers::get_instance().log_msg("http_codec::decode_body: Decoded message:", json_body); + message_body.json__body() = json_body; + } + } else { + loggers::get_instance().warning("http_codec::decode_body: No JSON codec found"); + json_body.raw() = CHARSTRING(body.lengthof(), (char*)static_cast(body)); + message_body.json__body() = json_body; + } + } else if (p["decode_str"].find("") != std::string::npos) { // Try to identify HTML + loggers::get_instance().log("http_codec::decode_body: Find html message"); + LibItsHttp__MessageBodyTypes::HtmlBody html_body; + message_body.html__body() = CHARSTRING(body.lengthof(), (char*)static_cast(body)); + } else { + loggers::get_instance().log("http_codec::decode_body: Use textBody as default"); + LibItsHttp__MessageBodyTypes::TextBody text_body; + message_body.text__body() = CHARSTRING(body.lengthof(), (char*)static_cast(body)); + } + } + + return 0; +} + +int http_codec::get_line(TTCN_Buffer& buffer, CHARSTRING& to, const bool concatenate_header_lines) { + unsigned int i = 0; + const unsigned char *cc_to = buffer.get_read_data(); + + // Sanity checks + if(buffer.get_read_len() == 0) { + return -1; + } + + while (true) { + // Skip spaces, and empty lines + for( ; i < buffer.get_read_len() && cc_to[i] != '\0' && cc_to[i] != '\r' && cc_to[i] != '\n'; i++); + if(i >= buffer.get_read_len()) { // No more characters to process + to = CHARSTRING(""); + return -1; + } else if(cc_to[i] == '\n') { // New line found, we don't care is '\r' is missing + if ((i > 0) && ((i + 1) < buffer.get_read_len()) && concatenate_header_lines && ((cc_to[i + 1] == ' ') || (cc_to[i + 1] == '\t'))) { + i += 1; // Skip it + } else { + to = CHARSTRING(i, (const char*)cc_to); + buffer.set_pos(buffer.get_pos() + i + 1); + return i == 0 ? 1 : 0; + } + } else { + if ((i + 1) < buffer.get_read_len() && cc_to[i + 1] != '\n') { + return -1; + } else if(i > 0 && (i + 2) < buffer.get_read_len() && concatenate_header_lines && (cc_to[i+2] == ' ' || cc_to[i+2] == '\t')) { + i += 2; + } else { + to = CHARSTRING(i, (const char*)cc_to); + buffer.set_pos(buffer.get_pos() + i + 2); + return i == 0 ? 1 : 0; + } + } + } // End of 'while' statement +} + +void http_codec::set_payload_codecs(const std::string& p_codecs) { + loggers::get_instance().log(">>> http_codec::set_payload_codecs: %s", p_codecs.c_str()); + + // Sanity check + if (p_codecs.length() == 0) { + return; + } + + // Extract codecs + try { + std::regex rgx("(\\w+):(\\w+)(;(\\w+):(\\w+))*"); + std::sregex_iterator begin(p_codecs.cbegin(), p_codecs.cend(), rgx); + std::sregex_iterator end = std::sregex_iterator(); + // E.g. 9 - xml - :held_codec - held_codec - ;html:html_codec - html:html_codec - html - :html_codec - html_codec + for (std::sregex_iterator it = begin; it != end; ++it) { + std::smatch m = *it; + loggers::get_instance().log("http_codec::set_payload_codecs: %d - %s - %s - %s - %s - %s - %s - %s - %s", m.size(), m[1].str().c_str(), m[2].str().c_str(), m[3].str().c_str(), m[4].str().c_str(), m[5].str().c_str(), m[6].str().c_str(), m[7].str().c_str(), m[8].str().c_str()); + for (unsigned int j = 1; j < m.size() - 1; j += 3) { // Exclude m[0] + loggers::get_instance().log("http_codec::set_payload_codecs: insert (%s, %s), j = %d", m[j].str().c_str(), m[j + 1].str().c_str(), j); + if (m[j].str().empty()) { + break; + } + std::string key(m[j].str()); + _codecs.insert(std::make_pair(key, std::unique_ptr >(codec_stack_builder::get_instance()->get_codec(m[j + 1].str().c_str())))); + } // End of 'for' statement + } // End of 'for' statement + loggers::get_instance().log("http_codec::set_payload_codecs: _codecs length=%d - %p", _codecs.size(), this); + } + catch(const std::logic_error& e){ + loggers::get_instance().warning("http_codec::set_payload_codecs: std::logic_error: %s", e.what()); + _codecs.clear(); + } +} diff --git a/ccsrc/Protocols/Http/http_codec.hh b/ccsrc/Protocols/Http/http_codec.hh new file mode 100644 index 0000000..2bfdf73 --- /dev/null +++ b/ccsrc/Protocols/Http/http_codec.hh @@ -0,0 +1,67 @@ +#pragma once + +#include + +#include "codec.hh" +#include "params.hh" + +class Base_Type; +class Record_Type; +class TTCN_Typedescriptor_t; +class TTCN_Buffer; + +namespace LibItsHttp__TypesAndValues { + class HttpMessage; + class Request; + class Response; + class HeaderLines; + class HeaderLine; +} +namespace LibItsHttp__MessageBodyTypes { + class HttpMessageBody; +} + +struct encoding_context { + unsigned int length; + unsigned char is_content_length_present; + + encoding_context() { reset(); }; + void reset() { length = -1; is_content_length_present = 0x00; }; +}; + +struct decoding_context { + unsigned int length; + unsigned char is_binary; + bool chunked; + + decoding_context() { reset(); }; + void reset() { length = -1; is_binary = 0x00; chunked = false; }; +}; + +class http_codec: public codec < +LibItsHttp__TypesAndValues::HttpMessage, +LibItsHttp__TypesAndValues::HttpMessage> +{ + encoding_context _ec; + decoding_context _dc; + std::map > > _codecs; +public: + explicit http_codec() : codec(), _ec(), _dc(), _codecs() { }; + virtual ~http_codec() { }; + + virtual int encode (const LibItsHttp__TypesAndValues::HttpMessage&, OCTETSTRING& data); + virtual int decode (const OCTETSTRING& data, LibItsHttp__TypesAndValues::HttpMessage&, params* params = NULL); + + void set_payload_codecs(const std::string& p_codecs); + +private: + int encode_request (const LibItsHttp__TypesAndValues::Request& p_request, TTCN_Buffer& p_encoding_buffer); + int encode_response (const LibItsHttp__TypesAndValues::Response& p_response, TTCN_Buffer& p_encoding_buffer); + int encode_body(const LibItsHttp__MessageBodyTypes::HttpMessageBody& p_message_body, OCTETSTRING& p_encoding_buffer, const std::string& p_content_type); + + int decode_headers(TTCN_Buffer& decoding_buffer, LibItsHttp__TypesAndValues::HeaderLines& headers, std::string& p_content_type); + int decode_header(CHARSTRING& header_line, LibItsHttp__TypesAndValues::HeaderLine& header); + int decode_body(TTCN_Buffer& decoding_buffer, LibItsHttp__MessageBodyTypes::HttpMessageBody& message_body, const std::string& p_content_type); + int get_line(TTCN_Buffer& buffer, CHARSTRING& to, const bool concatenate_header_lines = false); + +}; // End of class http_codec diff --git a/ccsrc/Protocols/Http/http_layer.cc b/ccsrc/Protocols/Http/http_layer.cc new file mode 100644 index 0000000..6602d69 --- /dev/null +++ b/ccsrc/Protocols/Http/http_layer.cc @@ -0,0 +1,129 @@ +#include "LibItsHttp_TypesAndValues.hh" + +#include "http_layer_factory.hh" +#include "codec_stack_builder.hh" + +#include "loggers.hh" + +#include "converter.hh" + +using namespace std; // Required for isnan() +#include "LibItsHttp_TypesAndValues.hh" +#include "LibItsHttp_TestSystem.hh" + +http_layer::http_layer(const std::string & p_type, const std::string & param) : t_layer(p_type), _params(), _device_mode{false} +{ + loggers::get_instance().log(">>> http_layer::http_layer: %s, %s", to_string().c_str(), param.c_str()); + // Setup parameters + params::convert(_params, param); + + params::const_iterator it = _params.find(params::codecs); + if (it != _params.cend()) { + _codec.set_payload_codecs(it->second); + } + it = _params.find(params::device_mode); + if (it != _params.cend()) { + _device_mode = (1 == converter::get_instance().string_to_int(it->second)); + } + it = _params.find(params::method); + if (it == _params.cend()) { + _params[params::method] = "POST"; + } + it = _params.find(params::uri); + if (it == _params.cend()) { + _params[params::uri] = "/"; + } + it = _params.find(params::host); + if (it == _params.cend()) { + _params[params::host] = "127.0.0.1"; + } + it = _params.find(params::content_type); + if (it == _params.cend()) { + _params[params::content_type] = "application/text"; + } +} + +void http_layer::sendMsg(const LibItsHttp__TypesAndValues::HttpMessage& p_http_message, params& p_param) { + loggers::get_instance().log_msg(">>> http_layer::sendMsg: ", p_http_message); + + // Encode HttpMessage + OCTETSTRING data; + _codec.encode(p_http_message, data); + send_data(data, _params); +} + +void http_layer::send_data(OCTETSTRING& data, params& params) { + loggers::get_instance().log_msg(">>> http_layer::send_data: ", data); + + if (_device_mode) { // Need to build an HTTP packet + loggers::get_instance().log("http_layer::send_data: Build http layer"); + TTCN_Buffer buffer; + buffer.put_cs(_params[params::method].c_str()); + buffer.put_c(' '); + buffer.put_cs(_params[params::uri].c_str()); + buffer.put_cs(" HTTP/1.1\r\n"); + buffer.put_cs("Host: "); + buffer.put_cs(_params[params::host].c_str()); + buffer.put_cs("\r\n"); + buffer.put_cs("Content-type: "); + buffer.put_cs(_params[params::content_type].c_str()); + buffer.put_cs("\r\n"); + buffer.put_cs("Content-length: "); + buffer.put_cs(static_cast(int2str(data.lengthof() + 2/*Stand for the last CRLF*/))); + buffer.put_cs("\r\n\r\n"); + buffer.put_os(data); + buffer.put_cs("\r\n"); + data = OCTETSTRING(buffer.get_len(), buffer.get_data()); + } + + loggers::get_instance().log_msg("http_layer::send_data: ", data); + send_to_all_layers(data, params); +} + +void http_layer::receive_data(OCTETSTRING& data, params& params) +{ + loggers::get_instance().log_msg(">>> http_layer::receive_data: ", data); + + // Decode HTTP message + LibItsHttp__TypesAndValues::HttpMessage http_message; + if (_codec.decode(data, http_message) == -1) { + loggers::get_instance().warning("http_layer::receive_data: Failed to decode data"); + return; + } + if (_device_mode) { + OCTETSTRING os; + if (http_message.ischosen(LibItsHttp__TypesAndValues::HttpMessage::ALT_response)) { + if (http_message.response().body().ispresent()) { + LibItsHttp__MessageBodyTypes::HttpMessageBody& body = static_cast(*http_message.response().body().get_opt_value()); + if (body.ischosen(LibItsHttp__MessageBodyTypes::HttpMessageBody::ALT_binary__body)) { + LibItsHttp__BinaryMessageBodyTypes::BinaryBody& binary = body.binary__body(); + if (binary.ischosen(LibItsHttp__BinaryMessageBodyTypes::BinaryBody::ALT_raw)) { + os = binary.raw(); + } else { + loggers::get_instance().warning("http_layer::receive_data: A raw binary payload is expected"); + } + } else if (body.ischosen(LibItsHttp__MessageBodyTypes::HttpMessageBody::ALT_html__body)){ + // TODO To be done + loggers::get_instance().warning("http_layer::receive_data: Not implemented yet"); + } else if (body.ischosen(LibItsHttp__MessageBodyTypes::HttpMessageBody::ALT_xml__body)){ + // TODO To be done + loggers::get_instance().warning("http_layer::receive_data: Not implemented yet"); + } else if (body.ischosen(LibItsHttp__MessageBodyTypes::HttpMessageBody::ALT_text__body)){ + // TODO To be done + loggers::get_instance().warning("http_layer::receive_data: Not implemented yet"); + } + receive_to_all_layers(os, params); + } else { + loggers::get_instance().warning("http_layer::receive_data: No body present"); + } + } else { + loggers::get_instance().warning("http_layer::receive_data: An HTTP response is expected"); + } + } else { + // Pass it to the ports + to_all_upper_ports(http_message, params); + } +} + +http_layer_factory http_layer_factory::_f; + diff --git a/ccsrc/Protocols/Http/http_layer.hh b/ccsrc/Protocols/Http/http_layer.hh new file mode 100644 index 0000000..ea55368 --- /dev/null +++ b/ccsrc/Protocols/Http/http_layer.hh @@ -0,0 +1,83 @@ +/*! + * \file http_layer.hh + * \brief Header file for ITS HTTP protocol layer. + * \author ETSI STF549 + * \copyright ETSI Copyright Notification + * No part may be reproduced except as authorized by written permission. + * The copyright and the foregoing restriction extend to reproduction in all media. + * All rights reserved. + * \version 0.1 + */ +#pragma once + +#include + +#include "t_layer.hh" + +#include "http_codec.hh" + +namespace LibItsHttp__TestSystem { + class HttpPort; +} + +namespace LibItsHttp__TypesAndValues { + class HttpMessage; //! Forward declaration of TITAN class +} + +class OCTETSTRING; //! Forward declaration of TITAN class + +/*! + * \class http_layer + * \brief This class provides a factory class to create an tcp_layer class instance + */ +class http_layer : public t_layer { + params _params; + http_codec _codec; + bool _device_mode; + +public: //! \publicsection + /*! + * \brief Specialised constructor + * Create a new instance of the http_layer class + * \param[in] p_type \todo + * \param[in] p_param \todo + */ + http_layer() : t_layer(), _params(), _device_mode{false} { }; + /*! + * \brief Specialised constructor + * Create a new instance of the http_layer class + * \param[in] p_type \todo + * \param[in] p_param \todo + */ + http_layer(const std::string& p_type, const std::string& p_param); + /*! + * \brief Default destructor + */ + virtual ~http_layer() { }; + + /*! + * \fn void sendMsg(const LibItsHttp__TypesAndValues::HttpMessage& p_http_message, params& p_param); + * \brief Send HTTP message to the lower layers + * \param[in] p_http_message The GeoNetworking message to be sent + * \param[in] p_params Some parameters to overwrite default value of the lower layers parameters + */ + void sendMsg(const LibItsHttp__TypesAndValues::HttpMessage& p_http_message, params& p_param); + + /*! + * \virtual + * \fn void send_data(OCTETSTRING& data, params& params); + * \brief Send bytes formated data to the lower layers + * \param[in] p_data The data to be sent + * \param[in] p_params Some parameters to overwrite default value of the lower layers parameters + */ + virtual void send_data(OCTETSTRING& data, params& params); + /*! + * \virtual + * \fn void receive_data(OCTETSTRING& data, params& params); + * \brief Receive bytes formated data from the lower layers + * \param[in] p_data The bytes formated data received + * \param[in] p_params Some lower layers parameters values when data was received + */ + virtual void receive_data(OCTETSTRING& data, params& info); +}; // End of class http_layer + diff --git a/ccsrc/Protocols/Http/http_layer_factory.hh b/ccsrc/Protocols/Http/http_layer_factory.hh new file mode 100644 index 0000000..b6ebebd --- /dev/null +++ b/ccsrc/Protocols/Http/http_layer_factory.hh @@ -0,0 +1,45 @@ +/*! + * \file http_layer_factory.hh + * \brief Header file for ITS Http protocol layer factory. + * \author ETSI STF525 + * \copyright ETSI Copyright Notification + * No part may be reproduced except as authorized by written permission. + * The copyright and the foregoing restriction extend to reproduction in all media. + * All rights reserved. + * \version 0.1 + */ +#pragma once + +#include "layer_stack_builder.hh" + +#include "http_layer.hh" + +/*! + * \class http_layer_factory + * \brief This class provides a factory class to create an http_layer class instance + */ +class http_layer_factory : public layer_factory { + static http_layer_factory _f; //! Reference to the unique instance of this class +public: //! \publicsection + /*! + * \brief Default constructor + * Create a new instance of the http_layer_factory class + * \remark The HTTP layer identifier is HTTP + */ + http_layer_factory() { + // Register factory + layer_stack_builder::register_layer_factory("HTTP", this); + }; + /*! + * \fn layer* create_layer(const std::string & type, const std::string & param); + * \brief Create the layers stack based on the provided layers stack description + * \param[in] p_type The provided layers stack description + * \param[in] p_params Optional parameters + * \return 0 on success, -1 otherwise + * \inline + */ + inline virtual layer* create_layer(const std::string& p_type, const std::string& p_param){ + return new http_layer(p_type, p_param); + }; +}; // End of class http_layer_factory + diff --git a/ccsrc/Protocols/Json/json_codec.cc b/ccsrc/Protocols/Json/json_codec.cc new file mode 100644 index 0000000..18d0dbb --- /dev/null +++ b/ccsrc/Protocols/Json/json_codec.cc @@ -0,0 +1,64 @@ +#include +#include +#include + +#include "json_codec_factory.hh" + +#include "loggers.hh" + +#include "LibItsHttp_JsonMessageBodyTypes.hh" + +int json_codec::encode (const LibItsHttp__JsonMessageBodyTypes::JsonBody& msg, OCTETSTRING& data) +{ + loggers::get_instance().log_msg(">>> json_codec::encode: ", (const Base_Type&)msg); + + TTCN_EncDec::clear_error(); + TTCN_EncDec::set_error_behavior(TTCN_EncDec::ET_ALL, TTCN_EncDec::EB_DEFAULT); + TTCN_Buffer encoding_buffer; + + loggers::get_instance().error("json_codec::encode: Not supported: "); + + loggers::get_instance().log("<<< json_codec::encode"); + return 0; +} + +int json_codec::decode (const OCTETSTRING& p_data, LibItsHttp__JsonMessageBodyTypes::JsonBody& msg, params* p_params) +{ + loggers::get_instance().log_msg(">>> json_codec::decode: p_data=", p_data); + + // Sanity checks + params::const_iterator it; + if (p_params == nullptr) { + loggers::get_instance().warning("json_codec::decode: Failed to access p_params (null pointer)"); + return -1; + } else { + it = p_params->find("decode_str"); + if (it == p_params->cend()) { + loggers::get_instance().warning("json_codec::decode: Failed to access p_params item (decode_str)"); + return -1; + } + } + + TTCN_EncDec::set_error_behavior(TTCN_EncDec::ET_ALL, TTCN_EncDec::EB_DEFAULT); + TTCN_EncDec::clear_error(); + // Remove data structure name... + int idx_begin = it->second.find(":"); + int idx_end = it->second.rfind("}") - 1; // Remove the last '}' + std::string str = it->second.substr(idx_begin + 1, idx_end - idx_begin); + // ..and create the decoding buffer + TTCN_Buffer decoding_buffer(OCTETSTRING(str.length(), (const unsigned char*)str.c_str())); + + if (it->second.find("{\"userInfo\"") != std::string::npos) { + LocationAPI__TypesAndValues::UserInfo user_info; + user_info.decode(LocationAPI__TypesAndValues::UserInfo_descr_, decoding_buffer, TTCN_EncDec::CT_JSON); + msg.userInfo() = user_info; + } else { + loggers::get_instance().warning("json_codec::decode: Unsupported variant"); + return -1; + } + + loggers::get_instance().log_msg("<<< json_codec::decode: ", (const Base_Type&)msg); + return 0; +} + +json_codec_factory json_codec_factory::_f; diff --git a/ccsrc/Protocols/Json/json_codec.hh b/ccsrc/Protocols/Json/json_codec.hh new file mode 100644 index 0000000..01073c5 --- /dev/null +++ b/ccsrc/Protocols/Json/json_codec.hh @@ -0,0 +1,23 @@ +#pragma once + +#include "codec.hh" +#include "params.hh" + +class Base_Type; +class TTCN_Typedescriptor_t; +class TTCN_Buffer; + +namespace LibItsHttp__JsonMessageBodyTypes { + class JsonBody; +} + +class json_codec: public codec +{ +public: + explicit json_codec() : codec() { }; + virtual ~json_codec() { }; + + virtual int encode (const LibItsHttp__JsonMessageBodyTypes::JsonBody&, OCTETSTRING& data); + virtual int decode (const OCTETSTRING& p_data, LibItsHttp__JsonMessageBodyTypes::JsonBody&, params* p_params = NULL); + +}; // End of class json_codec diff --git a/ccsrc/Protocols/Json/json_codec_factory.hh b/ccsrc/Protocols/Json/json_codec_factory.hh new file mode 100644 index 0000000..7cbd4f4 --- /dev/null +++ b/ccsrc/Protocols/Json/json_codec_factory.hh @@ -0,0 +1,46 @@ +/*! + * \file json_codec_factory.hh + * \brief Header file for ITS JSON/IP protocol codec factory. + * \author ETSI STF569 + * \copyright ETSI Copyright Notification + * No part may be reproduced except as authorized by written permission. + * The copyright and the foregoing restriction extend to reproduction in all media. + * All rights reserved. + * \version 0.1 + */ +#pragma once + +#include "codec_stack_builder.hh" + +#include "json_codec.hh" + +class Record_Type; //! TITAN forward declaration + +/*! + * \class json_codec_factory + * \brief This class provides a factory class to create an json_codec class instance + */ +class json_codec_factory: public codec_factory { + static json_codec_factory _f; //! Reference to the unique instance of this class +public: //! \publicsection + /*! + * \brief Default constructor + * Create a new instance of the json_codec_factory class + * \remark The HELD/IP codec identifier is HELD + */ + json_codec_factory() { + // register factory + codec_stack_builder::register_codec_factory("json_codec", this); + }; + /*! + * \fn codec* create_codec(const std::string & type, const std::string & param); + * \brief Create the codecs stack based on the provided codecs stack description + * \param[in] p_type The provided codecs stack description + * \param[in] p_params Optional parameters + * \return 0 on success, -1 otherwise + * \inline + */ + inline virtual codec* create_codec() { + return (codec*)new json_codec(); + }; +}; // End of class json_codec_factory diff --git a/ccsrc/Protocols/Pcap/pcap_cygwin_layer.cc b/ccsrc/Protocols/Pcap/pcap_cygwin_layer.cc new file mode 100644 index 0000000..5371cb1 --- /dev/null +++ b/ccsrc/Protocols/Pcap/pcap_cygwin_layer.cc @@ -0,0 +1,263 @@ +#if defined (__CYGWIN__) + +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "pcap_layer_factory.hh" + +#include "loggers.hh" + +#include + +typedef struct { + bpf_int32 tv_sec; /* seconds */ + bpf_int32 tv_usec; /* microseconds */ +}pcap_o_timeval; + +typedef struct pcap_o_pkthdr { + pcap_o_timeval ts; /* time stamp */ + bpf_u_int32 caplen; /* length of portion present */ + bpf_u_int32 len; /* length this packet (off wire) */ +}pcap_o_pkthdr; + +extern "C" int pcap_oid_get_request(pcap_t *p, bpf_u_int32 oid, void *data, size_t *lenp); + +static const char* _hexDigits = "0123456789ABCDEF"; +static char * _bin2hex(char * hex, size_t hlen, const char * bin, size_t blen) +{ + const unsigned char *b, *e; + char * s; + + // sanity check + if (hlen >= 0 && hlen < blen * 2) return NULL; + + b = (const unsigned char *)bin; + e = b + blen - 1; + s = hex + blen * 2; + if (s < hex + hlen) *s = 0; + for (; b <= e; e--){ + *(--s) = _hexDigits[(*e) & 0xF]; + *(--s) = _hexDigits[(*e) >> 4]; + } + return hex + blen * 2; +} + +pcap_layer::pcap_layer(const std::string& p_type, const std::string& param) : + layer(p_type), PORT(p_type.c_str()), _params(), _device(NULL), _running(FALSE), _time_key("pcap_layer::Handle_Fd_Event_Readable") { + loggers::get_instance().log(">>> pcap_layer::pcap_layer: %s, %s", p_type.c_str(), param.c_str()); + params::convert(_params, param); + + char error_buffer[PCAP_ERRBUF_SIZE]; + params::const_iterator it; + std::string nic; //network interface name + bpf_u_int32 mask; // subnet mask + bpf_u_int32 net; // ip address + + it = _params.find(params::nic); + if ((it == _params.end()) || it->second.empty()) { + loggers::get_instance().error("pcap_layer::pcap_layer: NIC must be specified"); + return; + } + + nic = std::string("\\Device\\NPF_{") + it->second + "}"; + + if (pcap_lookupnet(nic.c_str(), &net, &mask, error_buffer) != 0) { + loggers::get_instance().error("pcap_layer::pcap_layer: pcap_layer::pcap_layer: Failed to fetch newtork address for device %s", nic.c_str()); + } + loggers::get_instance().log("pcap_layer::pcap_layer: Device %s Network address: %d", nic.c_str(), net); + + // Open the device + _device = pcap_open_live(nic.c_str(), 65536, 1, 200, error_buffer); + if (_device == NULL) { + loggers::get_instance().error("pcap_layer::pcap_layer: Failed to open device %s", nic.c_str()); + return; + } + + // Setup filter + std::string filter = ""; + std::string mac_src; + it = _params.find(params::mac_src); + if (it != _params.end() && !it->second.empty()) { + mac_src = it->second; + } else { + // Not found + // determine it automatically +#ifndef OID_802_3_CURRENT_ADDRESS +#define OID_802_3_CURRENT_ADDRESS 0x01010102 +#endif + char mac[6] = {0}; + size_t l = sizeof(mac); + pcap_oid_get_request(_device, OID_802_3_CURRENT_ADDRESS, mac, &l); + char buf[13]; + *_bin2hex(buf, sizeof(buf), mac, 6) = 0; + mac_src = buf; + loggers::get_instance().user("pcap_layer::pcap_layer: local MAC is %s", mac_src.c_str()); + _params[params::mac_src] = mac_src; + } + + std::string mac_bc; + it = _params.find(params::mac_bc); + if (it != _params.end() && !it->second.empty()) + mac_bc = it->second; + else + mac_bc = "ffffffffffff"; + + if(mac_bc == mac_src || mac_src.empty()) + filter = "ether dst " + mac_bc; + else + filter = "( ether dst " + mac_bc + " or ether dst " + mac_src + " )"; + + if(! mac_src.empty()) + // Reject ITS messages sent by this component + filter += " and not ether src " + mac_src; + + // Add user defined filter + it = _params.find(std::string("filter")); + if ((it != _params.end()) && !it->second.empty()) { + filter += std::string(" ") + it->second; + } + + // Log final PCAP filter + loggers::get_instance().user("pcap_layer::pcap_layer: Filter: %s", filter.c_str()); + + // setup filter + { + struct bpf_program f = {0}; + if (pcap_compile(_device, &f, filter.c_str(), 1, PCAP_NETMASK_UNKNOWN) != 0) { + loggers::get_instance().error("pcap_layer::pcap_layer: Failed to compile PCAP filter"); + } else { + if (pcap_setfilter(_device, &f) != 0) { + loggers::get_instance().error("pcap_layer::pcap_layer: Failed to set PCAP filter"); + } + } + pcap_freecode(&f); + } + + _o_params.insert(std::pair(std::string("timestamp"), std::string())); + + // create pipe and run thread + if (pipe2(_fd, O_NONBLOCK) == -1) { + loggers::get_instance().error("pcap_layer::pcap_layer: Failed to create a pipe: %s", ::strerror(errno)); + } + // Pass the pipe handler to the polling procedure + loggers::get_instance().log("pcap_layer::pcap_layer: Call handler with descriptor %d", _fd[0]); + Handler_Add_Fd_Read(_fd[0]); + + // Create the reader thread + _thread = new std::thread(&pcap_layer::run, (void *)this); + if (_thread == NULL) { + loggers::get_instance().error("pcap_layer::pcap_layer: Failed to start offline thread"); + } + while (_running == FALSE) { + std::this_thread::sleep_for(std::chrono::milliseconds(500)); + } + // Thread was started + loggers::get_instance().log("<<< pcap_layer::pcap_layer"); +} // End of ctor + +pcap_layer::~pcap_layer() { + loggers::get_instance().log(">>> pcap_layer::~pcap_layer"); + + if (_device != NULL) { + if (_thread != NULL) { + _running = FALSE; + // Wait for the working thread to terminate + _thread->join(); + loggers::get_instance().log("pcap_layer::~pcap_layer: Thread were stops"); + // Cleanup + delete _thread; + close(_fd[0]); + close(_fd[1]); + } + pcap_close(_device); + } +} // End of dtor + +void* pcap_layer::run(void* p_this) { + pcap_layer& p = *static_cast(p_this); + return p.thread(); +} + +void* pcap_layer::thread() { + pcap_o_pkthdr *pkt_header; + const u_char *pkt_data; + unsigned char pkt_count = 0; + + loggers::get_instance().log(">>> pcap_layer::run"); + + _running = TRUE; + + // wait a bit before sending first packet + std::this_thread::sleep_for(std::chrono::milliseconds(500)); + + while (_running) { // Loop while _running flag is up + // get next frame + int result = pcap_next_ex(_device, (struct pcap_pkthdr**)&pkt_header, &pkt_data); + if(result == 0){ + continue; + } + if(result < 0){ + loggers::get_instance().log("<<< pcap_layer::run: error %s", pcap_geterr(_device)); + break; + } + + while(_running && !_resume.try_lock()) { + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + } +#if 0 + { + char buf[128]; + std::time_t t = pkt_header->ts.tv_sec; + std::tm * pt = std::localtime( &t ); + t = std::strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", pt); + std::sprintf(buf+t, ".%06ld", pkt_header->ts.tv_usec); + _o_params["timestamp"] = std::string(buf); + } +#else + _o_params["timestamp"] = std::to_string(pkt_header->ts.tv_usec); +#endif + _o_data = OCTETSTRING(pkt_header->len, pkt_data); + write(_fd[1], &pkt_count, 1);pkt_count++; + } + _running = FALSE; + loggers::get_instance().log("<<< pcap_layer::run"); + return NULL; +} + +void pcap_layer::send_data(OCTETSTRING& data, params& params) { + loggers::get_instance().log_msg(">>> pcap_layer::send_data: ", data); + if (pcap_sendpacket(_device, static_cast(data), data.lengthof()) == -1) { + loggers::get_instance().error("pcap_layer::send_data: Failed to send packet: %s", pcap_geterr(_device)); + } +} + +void pcap_layer::receive_data(OCTETSTRING& data, params& params) { + loggers::get_instance().log(">>> pcap_layer::receive_data: Received %d bytes", data.lengthof()); + loggers::get_instance().log_to_hexa("Packet dump", data); + + // Pass the packet to the upper layers + receive_to_all_layers(data, params); +} + +void pcap_layer::Handle_Fd_Event_Readable(int fd) { + char c[2]; + float duration; + loggers::get_instance().set_start_time(_time_key); + this->receive_data(_o_data, _o_params); + loggers::get_instance().set_stop_time(_time_key, duration); + read(_fd[0], &c, 1); + _resume.unlock(); +} + +pcap_layer_factory pcap_layer_factory::_f; + +#endif //__CYGWIN__ diff --git a/ccsrc/Protocols/Pcap/pcap_cygwin_layer.hh b/ccsrc/Protocols/Pcap/pcap_cygwin_layer.hh new file mode 100644 index 0000000..37008f5 --- /dev/null +++ b/ccsrc/Protocols/Pcap/pcap_cygwin_layer.hh @@ -0,0 +1,78 @@ +/*! + * \file pcap_layer.hh + * \brief Header file for ITS Offline Pcap port layer. + * \author ETSI STF525 + * \copyright ETSI Copyright Notification + * No part may be reproduced except as authorized by written permission. + * The copyright and the foregoing restriction extend to reproduction in all media. + * All rights reserved. + * \version 0.1 + */ +#pragma once + +#include +#include + +#include "t_layer.hh" +#include "params.hh" + +#include + +class PORT; //! Forward declaration of TITAN class + +typedef struct pcap pcap_t; + +/*! + * \class pcap_layer + * \brief This class provides description of ITS PCAP port protocol layer + */ +class pcap_layer : public layer, public PORT { + + params _params; //! Layer parameters + pcap_t* _device; //! Device handle + std::thread* _thread; //! Thread handle, used to read PCAP file instead of NIC, used in file mode + std::mutex _resume; + bool _running; //! Set to true when the thread is running, used in file mode + int _fd[2]; //! pipe to signal to Titan + + + OCTETSTRING _o_data; + params _o_params; + + std::string _time_key; + + static void* run(void* p_this); +public: + void* thread(void); +public: //! \publicsection + /*! + * \brief Specialised constructor + * Create a new instance of the pcap_layer class + * \param[in] p_type \todo + * \param[in] p_param \todo + */ + pcap_layer(const std::string& p_type, const std::string& param); + /*! + * \brief Default destructor + */ + virtual ~pcap_layer(); + + /*! + * \virtual + * \fn void send_data(OCTETSTRING& data, params& params); + * \brief Send bytes formated data to the lower layers + * \param[in] p_data The data to be sent + * \param[in] p_params Some parameters to overwrite default value of the lower layers parameters + */ + virtual void send_data(OCTETSTRING& data, params& params); + /*! + * \virtual + * \fn void receive_data(OCTETSTRING& data, params& params); + * \brief Receive bytes formated data from the lower layers + * \param[in] p_data The bytes formated data received + * \param[in] p_params Some lower layers parameters values when data was received + */ + virtual void receive_data(OCTETSTRING& data, params& info); + + void Handle_Fd_Event_Readable(int fd); +}; diff --git a/ccsrc/Protocols/Pcap/pcap_layer.cc b/ccsrc/Protocols/Pcap/pcap_layer.cc new file mode 100644 index 0000000..7b2f71d --- /dev/null +++ b/ccsrc/Protocols/Pcap/pcap_layer.cc @@ -0,0 +1,276 @@ +#if 0 +#include +#include +#include +#include +#include + +#include + +#include "pcap_layer_factory.hh" + +typedef struct pcap_pkthdr pcap_o_pkthdr; +typedef struct timeval pcap_o_timeval; + +#include "loggers.hh" + +pcap_layer::pcap_layer(const std::string& p_type, const std::string& param) : layer(p_type), PORT(p_type.c_str()), _params(), _device(NULL), _pcap_h(-1), _thread(NULL), _running(FALSE), _resume(), _sent_file(NULL), _time_key("pcap_layer::Handle_Fd_Event_Readable") { + bool online = false; + loggers::get_instance().log(">>> pcap_layer::pcap_layer: %s, %s", to_string().c_str(), param.c_str()); + _fd[0] = -1; _fd[1] = -1; + // Setup parameters + params::convert(_params, param); + //_params.log(); + // Prepare capture processing + char error_buffer[PCAP_ERRBUF_SIZE]; + params::const_iterator it = _params.find(params::nic); + if ((it != _params.end()) && !it->second.empty()) { // Use online capture + // Fetch the network address and network mask + bpf_u_int32 mask; // subnet mask + bpf_u_int32 net; // ip address + std::string nic; + online = true; + nic = _params[params::nic]; + if (pcap_lookupnet(nic.c_str(), &net, &mask, error_buffer) != 0) { + loggers::get_instance().error("pcap_layer::pcap_layer: pcap_layer::pcap_layer: Failed to fetch newtork address for device %s", nic.c_str()); + } + loggers::get_instance().log("pcap_layer::pcap_layer: Device %s Network address: %d", nic.c_str(), net); + // Open the device + _device = pcap_open_live(nic.c_str(), 65536, 1, 1000, error_buffer); // TODO Replace hard coded values by pcap_layer:: + if (_device == NULL) { + loggers::get_instance().error("pcap_layer::pcap_layer: Failed to open device %s", nic.c_str()); + } // else, continue + // Set non-blocking flag for the polling procedure + if (pcap_setnonblock(_device, 1, error_buffer) != 0) { + loggers::get_instance().error("pcap_layer::pcap_layer: Failed to set blocking mode: %s", error_buffer); + } + // Retrieve the device file handler + _pcap_h = pcap_get_selectable_fd(_device); + if (_pcap_h == -1) { + loggers::get_instance().error("pcap_layer::pcap_layer: Failed to get device handler"); + } + } else { + // Check file name + it = _params.find(std::string("file")); + if ((it != _params.cend()) && !it->second.empty()) { // Use offline capture + struct stat s = {0}; + if ((stat(_params["file"].c_str(), &s) != 0) || !S_ISREG(s.st_mode)) { + loggers::get_instance().error("pcap_layer::pcap_layer: Failed to acces PCAP file %s", _params["file"].c_str()); + } + // File exist, open it + _device = pcap_open_offline(_params["file"].c_str(), error_buffer); + if (_device == NULL) { + loggers::get_instance().error("pcap_layer::pcap_layer: Failed to open PCAP file %s", error_buffer); + } // else, continue + // Create the dump file for the sent packet based on the openned file name and the current time in milliseconds + it = _params.find("save_mode"); + if ((it != _params.cend()) && (it->second.compare("1") == 0)) { + unsigned long ms = std::chrono::system_clock::now().time_since_epoch() / std::chrono::milliseconds(1); + std::string ext("_" + std::to_string(ms)); + int i = _params["file"].find(".pcap"); + if (i > 0) { + std::string f(_params["file"].substr(0, i) + ext + ".pcap"); + loggers::get_instance().log("pcap_layer::pcap_layer: Save file name: %s", f.c_str()); + if ((_sent_file = pcap_dump_open(_device, f.c_str())) == NULL) { + loggers::get_instance().warning("pcap_layer::pcap_layer: Failed to open save file %s", f.c_str()); + } + } + } // else, nothing to do + } else { + loggers::get_instance().error("pcap_layer::pcap_layer: Failed to open PCAP file %s", error_buffer); + } + } + + // Setup filter + std::string filter = ""; + it = _params.find(params::mac_src); + if (it == _params.end()) { // Not found + loggers::get_instance().error("pcap_layer::pcap_layer: mac_src parameter not found, cannot continue"); + } else { + // Reject ITS messages sent by this component + filter = "not ether src " + _params[params::mac_src]; + // Accept ITS broadcasted to this componenet + filter += " and (ether dst " + _params[params::mac_src]; + // Accept ITS broadcasted messages + it = _params.find(params::mac_bc); + if ((it != _params.end()) && !it->second.empty()) { + filter += " or ether dst " + it->second + ")"; + } else { + filter += " or ether dst ffffffffffff) "; + } + // Add user defined filter + it = _params.find(std::string("filter")); + if ((it != _params.end()) && !it->second.empty()) { + filter += _params["filter"]; + } // else nothing to do + } + // Log final PCAP filter + loggers::get_instance().user("pcap_layer::pcap_layer: Filter: %s", filter.c_str()); + if (!filter.empty()) { + struct bpf_program f = {0}; + if (pcap_compile(_device, &f, filter.c_str(), 1, PCAP_NETMASK_UNKNOWN) != 0) { + loggers::get_instance().error("pcap_layer::pcap_layer: Failed to compile PCAP filter"); + } + if (pcap_setfilter(_device, &f) != 0) { + loggers::get_instance().error("pcap_layer::pcap_layer: Failed to set PCAP filter"); + } + pcap_freecode(&f); + } + + // Pass the device file handler to the polling procedure + if (_pcap_h != -1) { // Live capture + Handler_Add_Fd_Read(_pcap_h); + } else { // Offline capture or cygwin + // Create a pipe + if (pipe2(_fd, O_NONBLOCK) == -1) { + loggers::get_instance().error("pcap_layer::pcap_layer: Failed to create a pipe: %s", ::strerror(errno)); + } + if(online){ + _pcap_h = _fd[0]; + } + // Pass the pipe handler to the polling procedure + loggers::get_instance().log("pcap_layer::pcap_layer: Call handler with descriptor %d", _fd[0]); + Handler_Add_Fd_Read(_fd[0]); + // Create the offline reader thread + _thread = new std::thread(&pcap_layer::run, (void *)this); + if (_thread == NULL) { + loggers::get_instance().error("pcap_layer::pcap_layer: Failed to start offline thread"); + } + // Start it to dispatch packet to a pipe + while (_running == FALSE) { + std::this_thread::sleep_for(std::chrono::milliseconds(500)); + } + // Thread was started + loggers::get_instance().log("<<< pcap_layer::pcap_layer"); + } +} // End of ctor + +pcap_layer::~pcap_layer() { + loggers::get_instance().log(">>> pcap_layer::~pcap_layer"); + + if (_device != NULL) { + if (_thread != NULL) { + _running = FALSE; + // Wait for the working thread to terminate + _thread->join(); + loggers::get_instance().log("pcap_layer::~pcap_layer: Thread were stops"); + // Cleanup + delete _thread; + close(_fd[0]); + close(_fd[1]); + } + if (_sent_file != NULL) { + pcap_dump_close(_sent_file); + } + pcap_close(_device); + } +} // End of dtor + +void* pcap_layer::run(void* p_this) { + pcap_layer& p = *static_cast(p_this); + return p.thread(); +} + +void* pcap_layer::thread() { + loggers::get_instance().log(">>> pcap_layer::run"); + + // Wait a little bit before to start sending packet + std::this_thread::sleep_for(std::chrono::milliseconds(500)); + params::const_iterator it = _params.find("frame_offset"); + if ((it != _params.cend()) && (it->second.compare("0") != 0)) { + // TODO Try t use PCAP filter to start directly to the correct frame offset + /*try { + unsigned int offset = std::stoul(str_dec, &s); + // Skip frames + struct pcap_pkthdr *pkt_header; + const u_char *pkt_data; + int result = pcap_next_ex(_device, &pkt_header, &pkt_data); + if (result == 1) { // Succeed + } + } + catch (invalid_argument& i) { + } + catch (out_of_range& o) { + }*/ + } + // Let's go + _running = TRUE; + while (_running) { // Loop while _running flag is up + if (_resume.try_lock() == TRUE) { // Previous packet was consumed, lock for the next one + write(_fd[1], "\n", 1); // Any character will do the job + } else { // not ready yet + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + } + } + + loggers::get_instance().log("<<< pcap_layer::run"); + return NULL; +} + +void pcap_layer::send_data(OCTETSTRING& data, params& params) { + loggers::get_instance().log_msg(">>> pcap_layer::send_data: ", data); + + if (_pcap_h != -1) { // Check if offline mode is used + if (pcap_sendpacket(_device, static_cast(data), data.lengthof()) == -1) { + loggers::get_instance().error("pcap_layer::send_data: Failed to send packet: %s", pcap_geterr(_device)); + } + } else if (_sent_file != NULL) { + struct pcap_pkthdr hdr; + std::chrono::system_clock::time_point now = std::chrono::system_clock::now(); + std::chrono::milliseconds ms = std::chrono::duration_cast(now.time_since_epoch()); + hdr.ts.tv_sec = ms.count() / 1000; + hdr.ts.tv_usec = (ms.count() % 1000) * 1000; + hdr.caplen = data.lengthof(); + hdr.len = hdr.caplen; + pcap_dump((u_char *)_sent_file, &hdr, static_cast(data)); + } else { + loggers::get_instance().log("pcap_layer::send_data: Offline mode, operation was skipped"); + } +} + +void pcap_layer::receive_data(OCTETSTRING& data, params& params) { + loggers::get_instance().log(">>> pcap_layer::receive_data: Received %d bytes", data.lengthof()); + loggers::get_instance().log_to_hexa("Packet dump", data); + + // Pass the packet to the upper layers + receive_to_all_layers(data, params); +} + +void pcap_layer::Handle_Fd_Event_Readable(int fd) { + //loggers::get_instance().log(">>> pcap_layer::Handle_Fd_Event_Readable: %d", fd); + + pcap_o_pkthdr *pkt_header; + const u_char *pkt_data; + int result = pcap_next_ex(_device, (struct pcap_pkthdr**)&pkt_header, &pkt_data); + if (result == 1) { // Succeed + if (pkt_header->caplen > 14) { // Reject too small packet + //loggers::get_instance().log("pcap_layer::Handle_Fd_Event_Readable: %.6d - %d", pkt_header->ts.tv_usec, pkt_header->len); + // Fill parameters from PCAP layer + params params; + params.insert(std::pair(std::string("timestamp"), std::to_string(pkt_header->ts.tv_usec))); + // Process the packet at this layer + OCTETSTRING os(pkt_header->caplen, pkt_data); + //loggers::get_instance().log_to_hexa("pcap_layer::Handle_Fd_Event_Readable: ", os); + // TODO Case of caplen != len !!! + float duration; + loggers::get_instance().set_start_time(_time_key); + this->receive_data(os, params); // TODO Check execution time for decoding operation + loggers::get_instance().set_stop_time(_time_key, duration); + } + } // else, skip the packet + // Specific to offline mode + if (_fd[0] != -1) { // Check if offline mode is used + //loggers::get_instance().log("pcap_layer::Handle_Fd_Event_Readable: Read pipe"); + char c[2]; + read(_fd[0], &c, 1); + if (result == -2) { // End of file, therminate worker thread + _running = FALSE; + } + //loggers::get_instance().log("pcap_layer::Handle_Fd_Event_Readable: pcap_next_ex failed: result=%d", result); + _resume.unlock(); + } // else, nothing to do +} + +pcap_layer_factory pcap_layer_factory::_f; + +#endif // !CYGWIN diff --git a/ccsrc/Protocols/Pcap/pcap_layer.hh b/ccsrc/Protocols/Pcap/pcap_layer.hh new file mode 100644 index 0000000..098e1de --- /dev/null +++ b/ccsrc/Protocols/Pcap/pcap_layer.hh @@ -0,0 +1,5 @@ +#if defined (__CYGWIN__) + #include "pcap_cygwin_layer.hh" +#else + #include "pcap_linux_layer.hh" +#endif diff --git a/ccsrc/Protocols/Pcap/pcap_layer_factory.hh b/ccsrc/Protocols/Pcap/pcap_layer_factory.hh new file mode 100644 index 0000000..0c3c3cc --- /dev/null +++ b/ccsrc/Protocols/Pcap/pcap_layer_factory.hh @@ -0,0 +1,45 @@ +/*! + * \file pcap_layer_factory.hh + * \brief Header file for Pcap layer factory. + * \author ETSI STF525 + * \copyright ETSI Copyright Notification + * No part may be reproduced except as authorized by written permission. + * The copyright and the foregoing restriction extend to reproduction in all media. + * All rights reserved. + * \version 0.1 + */ +#pragma once + +#include "layer_stack_builder.hh" + +#include "pcap_layer.hh" + +/*! + * \class pcap_layer_factory + * \brief This class provides a factory class to create an pcap_layer class instance + */ +class pcap_layer_factory: public layer_factory { + static pcap_layer_factory _f; //! Reference to the unique instance of this class +public: //! \publicsection + /*! + * \brief Default constructor + * Create a new instance of the udp_layer_factory class + * \remark The PCAP layer identifier is PCAP + */ + pcap_layer_factory() { + // register factory + layer_stack_builder::register_layer_factory("PCAP", this); + }; + /*! + * \fn layer* create_layer(const std::string & type, const std::string & param); + * \brief Create the layers stack based on the provided layers stack description + * \param[in] p_type The provided layers stack description + * \param[in] p_params Optional parameters + * \return 0 on success, -1 otherwise + * \inline + */ + inline virtual layer* create_layer(const std::string& p_type, const std::string& p_param) { + return new pcap_layer(p_type, p_param); + }; +}; // End of class pcap_layer_factory + diff --git a/ccsrc/Protocols/Pcap/pcap_linux_layer.cc b/ccsrc/Protocols/Pcap/pcap_linux_layer.cc new file mode 100644 index 0000000..07312df --- /dev/null +++ b/ccsrc/Protocols/Pcap/pcap_linux_layer.cc @@ -0,0 +1,199 @@ +#if !defined (__CYGWIN__) +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "pcap_layer_factory.hh" + +#include "loggers.hh" + +static const char* _hexDigits = "0123456789ABCDEF"; +static char * _bin2hex(char * hex, size_t hlen, const char * bin, size_t blen) +{ + const unsigned char *b, *e; + char * s; + + // sanity check + if (hlen >= 0 && hlen < blen * 2) return NULL; + + b = (const unsigned char *)bin; + e = b + blen - 1; + s = hex + blen * 2; + if (s < hex + hlen) *s = 0; + for (; b <= e; e--){ + *(--s) = _hexDigits[(*e) & 0xF]; + *(--s) = _hexDigits[(*e) >> 4]; + } + return hex + blen * 2; +} + + +pcap_layer::pcap_layer(const std::string& p_type, const std::string& param) +: layer(p_type), PORT(p_type.c_str()), _params(), _device(NULL), _pcap_h(-1), + _time_key("pcap_layer::Handle_Fd_Event_Readable") +{ + char error_buffer[PCAP_ERRBUF_SIZE]; + params::const_iterator it; + std::string nic; + + loggers::get_instance().log(">>> pcap_layer::pcap_layer: %s, %s", to_string().c_str(), param.c_str()); + // Setup parameters + params::convert(_params, param); + // Prepare capture processing + it = _params.find(params::nic); + if ((it == _params.end()) || it->second.empty()) { // Use online capture + loggers::get_instance().error("pcap_layer::pcap_layer: NIC name must be specified"); + return; + } + + nic = _params[params::nic]; + { + bpf_u_int32 net, mask; // ip address and subnet mask + if (pcap_lookupnet(nic.c_str(), &net, &mask, error_buffer) != 0) { + loggers::get_instance().error("pcap_layer::pcap_layer: Failed to fetch newtork address for device %s", nic.c_str()); + }else{ + loggers::get_instance().log("pcap_layer::pcap_layer: Device %s Network address: %d", nic.c_str(), net); + } + } + // Open the device + _device = pcap_open_live(nic.c_str(), 65536, 1, 100, error_buffer); // TODO Replace hard coded values by pcap_layer:: + if (_device == NULL) { + loggers::get_instance().error("pcap_layer::pcap_layer: Failed to open device %s", nic.c_str()); + return; + } // else, continue + // Set non-blocking flag for the polling procedure + if (pcap_setnonblock(_device, 1, error_buffer) != 0) { + loggers::get_instance().error("pcap_layer::pcap_layer: Failed to set blocking mode: %s", error_buffer); + } + // Retrieve the device file handler + _pcap_h = pcap_get_selectable_fd(_device); + if (_pcap_h == -1) { + loggers::get_instance().error("pcap_layer::pcap_layer: Failed to get device handler"); + } + + // Setup filter + std::string filter = ""; + std::string mac_src; + it = _params.find(params::mac_src); + if (it != _params.end()) { // Use online capture + mac_src = it->second; + } else{ + // detect MAC address of NIC + struct ifreq ifr; + memset(&ifr, 0, sizeof(ifr)); + nic.copy(ifr.ifr_name, sizeof(ifr.ifr_name)); + if (ioctl(_pcap_h, SIOCGIFHWADDR, &ifr) == -1) { + loggers::get_instance().error("pcap_layer::pcap_layer: Failed to get device MAC address"); + }else{ + char buf[13]; + *_bin2hex(buf, sizeof(buf), ifr.ifr_hwaddr.sa_data, 6) = 0; + mac_src = buf; + loggers::get_instance().user("pcap_layer::pcap_layer: local MAC is %s", mac_src.c_str()); + _params[params::mac_src] = mac_src; + } + } + + // Accept ITS broadcasted messages + std::string mac_bc; + it = _params.find(params::mac_bc); + if (it != _params.end() && !it->second.empty()) { + mac_bc = it->second; + } else { + mac_bc = "ffffffffffff"; + } + + if ((mac_bc == mac_src) || mac_src.empty()) { + filter = "ether dst " + mac_bc; + } else { + filter = "( ether dst " + mac_bc + " or ether dst " + mac_src + " )"; + } + + if (!mac_src.empty()) { + // Reject ITS messages sent by this component + filter += " and not ether src " + mac_src; + } + + // Add user defined filter + it = _params.find(std::string("filter")); + if ((it != _params.end()) && !it->second.empty()) { + filter += std::string(" ") + it->second; + } + // Log final PCAP filter + loggers::get_instance().user("pcap_layer::pcap_layer: Filter: %s", filter.c_str()); + + { + struct bpf_program f = {0}; + if (pcap_compile(_device, &f, filter.c_str(), 1, PCAP_NETMASK_UNKNOWN) != 0) { + loggers::get_instance().error("pcap_layer::pcap_layer: Failed to compile PCAP filter"); + }else{ + if (pcap_setfilter(_device, &f) != 0) { + loggers::get_instance().error("pcap_layer::pcap_layer: Failed to set PCAP filter"); + } + } + pcap_freecode(&f); + } + + // Pass the device file handler to the polling procedure + Handler_Add_Fd_Read(_pcap_h); +} // End of ctor + +pcap_layer::~pcap_layer() { + loggers::get_instance().log(">>> pcap_layer::~pcap_layer"); + + if (_device != NULL) { + pcap_close(_device); + } +} // End of dtor + + +void pcap_layer::send_data(OCTETSTRING& data, params& params) { + loggers::get_instance().log_msg(">>> pcap_layer::send_data: ", data); + + if (pcap_sendpacket(_device, static_cast(data), data.lengthof()) == -1) { + loggers::get_instance().error("pcap_layer::send_data: Failed to send packet: %s", pcap_geterr(_device)); + } +} + +void pcap_layer::receive_data(OCTETSTRING& data, params& params) { + loggers::get_instance().log(">>> pcap_layer::receive_data: Received %d bytes", data.lengthof()); + loggers::get_instance().log_to_hexa("Packet dump", data); + + // Pass the packet to the upper layers + receive_to_all_layers(data, params); +} + +void pcap_layer::Handle_Fd_Event_Readable(int fd) { + //loggers::get_instance().log(">>> pcap_layer::Handle_Fd_Event_Readable: %d", fd); + + pcap_pkthdr *pkt_header; + const u_char *pkt_data; + int result = pcap_next_ex(_device, &pkt_header, &pkt_data); + if (result == 1) { // Succeed + if (pkt_header->caplen > 14) { // Reject too small packet + //loggers::get_instance().log("pcap_layer::Handle_Fd_Event_Readable: %.6d - %d", pkt_header->ts.tv_usec, pkt_header->len); + // Fill parameters from PCAP layer + params params; + params.insert(std::pair(std::string("timestamp"), std::to_string(pkt_header->ts.tv_usec))); + // Process the packet at this layer + OCTETSTRING os(pkt_header->caplen, pkt_data); + //loggers::get_instance().log_to_hexa("pcap_layer::Handle_Fd_Event_Readable: ", os); + // TODO Case of caplen != len !!! + float duration; + loggers::get_instance().set_start_time(_time_key); + this->receive_data(os, params); // TODO Check execution time for decoding operation + loggers::get_instance().set_stop_time(_time_key, duration); + } + } // else, skip the packet +} + +pcap_layer_factory pcap_layer_factory::_f; + +#endif // !CYGWIN diff --git a/ccsrc/Protocols/Pcap/pcap_linux_layer.hh b/ccsrc/Protocols/Pcap/pcap_linux_layer.hh new file mode 100644 index 0000000..f09fe59 --- /dev/null +++ b/ccsrc/Protocols/Pcap/pcap_linux_layer.hh @@ -0,0 +1,62 @@ +/*! + * \file pcap_layer.hh + * \brief Header file for ITS Pcap port layer. + * \author ETSI STF525 + * \copyright ETSI Copyright Notification + * No part may be reproduced except as authorized by written permission. + * The copyright and the foregoing restriction extend to reproduction in all media. + * All rights reserved. + * \version 0.1 + */ +#pragma once + +#include + +#include "t_layer.hh" +#include "params.hh" + +class PORT; //! Forward declaration of TITAN class + +/*! + * \class pcap_layer + * \brief This class provides description of ITS PCAP port protocol layer + */ +class pcap_layer : public layer, public PORT { + params _params; //! Layer parameters + pcap_t* _device; //! Device handle + int _pcap_h; //! PCAP instance handle + pcap_dumper_t* _sent_file; //! Write file handle to save sent packet, used in file mode + std::string _time_key; //! \todo + +public: //! \publicsection + /*! + * \brief Specialised constructor + * Create a new instance of the pcap_layer class + * \param[in] p_type \todo + * \param[in] p_param \todo + */ + pcap_layer(const std::string& p_type, const std::string& param); + /*! + * \brief Default destructor + */ + virtual ~pcap_layer(); + + /*! + * \virtual + * \fn void send_data(OCTETSTRING& data, params& params); + * \brief Send bytes formated data to the lower layers + * \param[in] p_data The data to be sent + * \param[in] p_params Some parameters to overwrite default value of the lower layers parameters + */ + virtual void send_data(OCTETSTRING& data, params& params); + /*! + * \virtual + * \fn void receive_data(OCTETSTRING& data, params& params); + * \brief Receive bytes formated data from the lower layers + * \param[in] p_data The bytes formated data received + * \param[in] p_params Some lower layers parameters values when data was received + */ + virtual void receive_data(OCTETSTRING& data, params& info); + + void Handle_Fd_Event_Readable(int fd); +}; diff --git a/ccsrc/Protocols/Pcap/pcap_offline_layer.cc b/ccsrc/Protocols/Pcap/pcap_offline_layer.cc new file mode 100644 index 0000000..0f61bed --- /dev/null +++ b/ccsrc/Protocols/Pcap/pcap_offline_layer.cc @@ -0,0 +1,228 @@ +#if defined (__CYGWIN__) +#define _GNU_SOURCE +#endif +#include +#include +#include +#include +#include + +#include + +#include "pcap_offline_layer_factory.hh" + +#include "loggers.hh" + +#include + +#ifdef __CYGWIN__ +typedef struct { + bpf_int32 tv_sec; /* seconds */ + bpf_int32 tv_usec; /* microseconds */ +}pcap_o_timeval; + +typedef struct pcap_o_pkthdr { + pcap_o_timeval ts; /* time stamp */ + bpf_u_int32 caplen; /* length of portion present */ + bpf_u_int32 len; /* length this packet (off wire) */ +}pcap_o_pkthdr; +#else +typedef struct pcap_pkthdr pcap_o_pkthdr; +typedef struct timeval pcap_o_timeval; +#endif + +pcap_offline_layer::pcap_offline_layer(const std::string& p_type, const std::string& param) : + layer(p_type), PORT(p_type.c_str()), _params(), _device(NULL), _running(FALSE), _time_key("pcap_offline_layer::Handle_Fd_Event_Readable") { + loggers::get_instance().log(">>> pcap_offline_layer::pcap_offline_layer: %s, %s", p_type.c_str(), param.c_str()); + params::convert(_params, param); + + _o_params.insert(std::pair(std::string("timestamp"), std::string())); + + char error_buffer[PCAP_ERRBUF_SIZE]; + params::const_iterator it; + + it = _params.find(std::string("realtime")); + _realtime = ((it != _params.end()) && !it->second.empty()); + + it = _params.find(std::string("loop")); + _loop = ((it != _params.end()) && !it->second.empty()); + + it = _params.find(std::string("file")); + if ((it != _params.end()) && !it->second.empty()) { + const std::string& file = it->second; + _device = pcap_open_offline(file.c_str(), error_buffer); + if (_device) { + + // Add user defined filter + it = _params.find(std::string("filter")); + if ((it != _params.end()) && !it->second.empty()) { + const std::string& filter = it->second; + // Log final PCAP filter + loggers::get_instance().user("pcap_offline_layer::pcap_offline_layer: Filter: %s", filter.c_str()); + struct bpf_program f = {0}; + if (pcap_compile(_device, &f, filter.c_str(), 1, PCAP_NETMASK_UNKNOWN) != 0) { + loggers::get_instance().error("pcap_offline_layer::pcap_offline_layer: Failed to compile PCAP filter"); + }else{ + if (pcap_setfilter(_device, &f) != 0) { + loggers::get_instance().error("pcap_offline_layer::pcap_offline_layer: Failed to set PCAP filter"); + } + } + pcap_freecode(&f); + } + + // create pipe and run thread + if (pipe2(_fd, O_NONBLOCK) == -1) { + loggers::get_instance().error("pcap_offline_layer::pcap_offline_layer: Failed to create a pipe: %s", ::strerror(errno)); + } + // Pass the pipe handler to the polling procedure + loggers::get_instance().log("pcap_offline_layer::pcap_offline_layer: Call handler with descriptor %d", _fd[0]); + Handler_Add_Fd_Read(_fd[0]); + // Create the offline reader thread + _thread = new std::thread(&pcap_offline_layer::run, (void *)this); + if (_thread == NULL) { + loggers::get_instance().error("pcap_offline_layer::pcap_offline_layer: Failed to start offline thread"); + } + while (_running == FALSE) { + std::this_thread::sleep_for(std::chrono::milliseconds(500)); + } + // Thread was started + loggers::get_instance().log("<<< pcap_offline_layer::pcap_offline_layer"); + } + } +} // End of ctor + +pcap_offline_layer::~pcap_offline_layer() { + loggers::get_instance().log(">>> pcap_offline_layer::~pcap_offline_layer"); + + if (_device != NULL) { + if (_thread != NULL) { + _running = FALSE; + // Wait for the working thread to terminate + _thread->join(); + loggers::get_instance().log("pcap_offline_layer::~pcap_offline_layer: Thread were stops"); + // Cleanup + delete _thread; + close(_fd[0]); + close(_fd[1]); + } + pcap_close(_device); + } +} // End of dtor + +void* pcap_offline_layer::run(void* p_this) { + pcap_offline_layer& p = *static_cast(p_this); + return p.thread(); +} + + +static long timeval_diff (const pcap_o_timeval &x, const pcap_o_timeval &y) +{ + pcap_o_timeval z = y; + /* Perform the carry for the later subtraction by updating y. */ + if (x.tv_usec < y.tv_usec) { + int nsec = (y.tv_usec - x.tv_usec) / 1000000 + 1; + z.tv_usec -= 1000000 * nsec; + z.tv_sec += nsec; + } + if (x.tv_usec - z.tv_usec > 1000000) { + int nsec = (x.tv_usec - z.tv_usec) / 1000000; + z.tv_usec += 1000000 * nsec; + z.tv_sec -= nsec; + } + + return (x.tv_sec - z.tv_sec) * 1000 + ((x.tv_usec - z.tv_usec)/1000); +} + +void* pcap_offline_layer::thread() { + pcap_o_pkthdr *pkt_header; + pcap_o_pkthdr lh; + const u_char *pkt_data; + unsigned char pkt_count = 0; + + loggers::get_instance().log(">>> pcap_offline_layer::run"); + + memset(&lh, 0, sizeof(lh)); + + _running = TRUE; + + int delay = 1000; + params::const_iterator it; + it = _params.find(std::string("delay")); + if(it != _params.cend()){ + delay = std::stoi(it->second); + } + + // wait a bit before sending first packet + std::this_thread::sleep_for(std::chrono::milliseconds(delay)); + + while (_running) { // Loop while _running flag is up + // get next frame + int result = pcap_next_ex(_device, (struct pcap_pkthdr**)&pkt_header, &pkt_data); + if(result == 2){ + if(_loop){ + + }else{ + _running = FALSE; + return NULL; + } + } + if(_realtime) { + // wait for next packet timestamp + if(lh.ts.tv_sec|lh.ts.tv_usec){ + long diff = timeval_diff(pkt_header->ts, lh.ts); + if(diff > 0) { + loggers::get_instance().log("<<< pcap_offline_layer::run: Wait %d msec", diff); + std::this_thread::sleep_for(std::chrono::milliseconds(diff)); + loggers::get_instance().log("<<< pcap_offline_layer::run: Wait done"); + } + } + } + while(_running && !_resume.try_lock()) { + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + } + lh = *pkt_header; +#if 0 + { + char buf[128]; + std::time_t t = pkt_header->ts.tv_sec; + std::tm * pt = std::localtime( &t ); + t = std::strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", pt); + std::sprintf(buf+t, ".%06ld", pkt_header->ts.tv_usec); + _o_params["timestamp"] = std::string(buf); + } +#else + _o_params["timestamp"] = std::to_string(pkt_header->ts.tv_usec); +#endif + _o_data = OCTETSTRING(pkt_header->len, pkt_data); + write(_fd[1], &pkt_count, 1);pkt_count++; + } + + loggers::get_instance().log("<<< pcap_offline_layer::run"); + return NULL; +} + +void pcap_offline_layer::send_data(OCTETSTRING& data, params& params) { + loggers::get_instance().log("pcap_offline_layer::send_data: Offline mode, operation was skipped"); +} + +void pcap_offline_layer::receive_data(OCTETSTRING& data, params& params) { + loggers::get_instance().log(">>> pcap_offline_layer::receive_data: Received %d bytes", data.lengthof()); + loggers::get_instance().log_to_hexa("Packet dump", data); + + // Pass the packet to the upper layers + receive_to_all_layers(data, params); +} + +void pcap_offline_layer::Handle_Fd_Event_Readable(int fd) { + //loggers::get_instance().log(">>> pcap_offline_layer::Handle_Fd_Event_Readable: %d", fd); + char c[2]; + float duration; + // Process the packet at this layer + loggers::get_instance().set_start_time(_time_key); + this->receive_data(_o_data, _o_params); + loggers::get_instance().set_stop_time(_time_key, duration); + read(_fd[0], &c, 1); + _resume.unlock(); +} + +pcap_offline_layer_factory pcap_offline_layer_factory::_f; diff --git a/ccsrc/Protocols/Pcap/pcap_offline_layer.hh b/ccsrc/Protocols/Pcap/pcap_offline_layer.hh new file mode 100644 index 0000000..0886683 --- /dev/null +++ b/ccsrc/Protocols/Pcap/pcap_offline_layer.hh @@ -0,0 +1,78 @@ +/*! + * \file pcap_offline_layer.hh + * \brief Header file for ITS Offline Pcap port layer. + * \author ETSI STF525 + * \copyright ETSI Copyright Notification + * No part may be reproduced except as authorized by written permission. + * The copyright and the foregoing restriction extend to reproduction in all media. + * All rights reserved. + * \version 0.1 + */ +#pragma once + +#include +#include + +#include "t_layer.hh" +#include "params.hh" + +#include + +class PORT; //! Forward declaration of TITAN class + +typedef struct pcap pcap_t; + +/*! + * \class pcap_layer + * \brief This class provides description of ITS PCAP port protocol layer + */ +class pcap_offline_layer : public layer, public PORT { + params _params; //! Layer parameters + pcap_t* _device; //! Device handle + std::thread* _thread; //! Thread handle, used to read PCAP file instead of NIC, used in file mode + std::mutex _resume; + bool _running; //! Set to true when the thread is running, used in file mode + bool _realtime; //! Set to true if realtime delay shall be added between packets + bool _loop; //! Set to true if playback shall be looped + int _fd[2]; //! pipe to signal to Titan + + params _o_params; + OCTETSTRING _o_data; + + std::string _time_key; + + static void* run(void* p_this); +public: + void* thread(void); +public: //! \publicsection + /*! + * \brief Specialised constructor + * Create a new instance of the pcap_layer class + * \param[in] p_type \todo + * \param[in] p_param \todo + */ + pcap_offline_layer(const std::string& p_type, const std::string& param); + /*! + * \brief Default destructor + */ + virtual ~pcap_offline_layer(); + + /*! + * \virtual + * \fn void send_data(OCTETSTRING& data, params& params); + * \brief Send bytes formated data to the lower layers + * \param[in] p_data The data to be sent + * \param[in] p_params Some parameters to overwrite default value of the lower layers parameters + */ + virtual void send_data(OCTETSTRING& data, params& params); + /*! + * \virtual + * \fn void receive_data(OCTETSTRING& data, params& params); + * \brief Receive bytes formated data from the lower layers + * \param[in] p_data The bytes formated data received + * \param[in] p_params Some lower layers parameters values when data was received + */ + virtual void receive_data(OCTETSTRING& data, params& info); + + void Handle_Fd_Event_Readable(int fd); +}; diff --git a/ccsrc/Protocols/Pcap/pcap_offline_layer_factory.hh b/ccsrc/Protocols/Pcap/pcap_offline_layer_factory.hh new file mode 100644 index 0000000..ac6e6a6 --- /dev/null +++ b/ccsrc/Protocols/Pcap/pcap_offline_layer_factory.hh @@ -0,0 +1,45 @@ +/*! + * \file pcap_offline_layer_factory.hh + * \brief Header file for Pcap layer factory. + * \author ETSI STF525 + * \copyright ETSI Copyright Notification + * No part may be reproduced except as authorized by written permission. + * The copyright and the foregoing restriction extend to reproduction in all media. + * All rights reserved. + * \version 0.1 + */ +#pragma once + +#include "layer_stack_builder.hh" + +#include "pcap_offline_layer.hh" + +/*! + * \class pcap_offline_layer_factory + * \brief This class provides a factory class to create an pcap_offline_layer class instance + */ +class pcap_offline_layer_factory: public layer_factory { + static pcap_offline_layer_factory _f; //! Reference to the unique instance of this class +public: //! \publicsection + /*! + * \brief Default constructor + * Create a new instance of the udp_layer_factory class + * \remark The PCAP layer identifier is PCAP + */ + pcap_offline_layer_factory() { + // register factory + layer_stack_builder::register_layer_factory("PCAP_FILE", this); + }; + /*! + * \fn layer* create_layer(const std::string & type, const std::string & param); + * \brief Create the layers stack based on the provided layers stack description + * \param[in] p_type The provided layers stack description + * \param[in] p_params Optional parameters + * \return 0 on success, -1 otherwise + * \inline + */ + inline virtual layer* create_layer(const std::string& p_type, const std::string& p_param) { + return new pcap_offline_layer(p_type, p_param); + }; +}; // End of class pcap_offline_layer_factory + diff --git a/ccsrc/Protocols/Tcp/tcp_layer.cc b/ccsrc/Protocols/Tcp/tcp_layer.cc new file mode 100644 index 0000000..4f86103 --- /dev/null +++ b/ccsrc/Protocols/Tcp/tcp_layer.cc @@ -0,0 +1,203 @@ +#include +#include +#include +#include +#include + +#include "tcp_layer_factory.hh" + +#include "converter.hh" + +#include "loggers.hh" + +tcp_layer::tcp_layer(const std::string & p_type, const std::string & param) : layer(p_type), SSL_Socket(), PORT(p_type.c_str()), _params(), _client_id{-1}, _time_key("tcp_layer::Handle_Fd_Event_Readable"), _reconnect_on_send{false} { + loggers::get_instance().log(">>> tcp_layer::tcp_layer: %s, %s", to_string().c_str(), param.c_str()); + // Setup parameters + params::convert(_params, param); + _params.log(); + + set_socket_debugging(false); + params::const_iterator it = _params.find(params::debug); + if (it == _params.cend()) { + _params.insert(std::pair(std::string("debug"), "0")); + } else if (it->second.compare("1") == 0) { + set_socket_debugging(true); + } + bool server_mode = false; + it = _params.find(params::server_mode); + if (it != _params.cend()) { + server_mode = (1 == converter::get_instance().string_to_int(it->second)); + } + it = _params.find(params::server); + if (it == _params.cend()) { + _params.insert(std::pair(std::string("server"), "127.0.0.1")); // TODO Try using params::server instead of std::string("server") + } + if (!parameter_set(params::server.c_str(), _params[params::server].c_str())) { + loggers::get_instance().warning("tcp_layer::set_parameter: Unprocessed parameter: %s", params::server.c_str()); + } + set_ssl_use_ssl(false); + it = _params.find(params::use_ssl); + if (it == _params.cend()) { + _params.insert(std::pair(std::string("use_ssl"), "0")); + } else if (it->second.compare("1") == 0) { + set_ssl_use_ssl(true); + } + it = _params.find(params::port); + if (it == _params.cend()) { + if (_params[params::use_ssl].compare("0") == 0) { // Use standard HTTP port + _params.insert(std::pair(std::string("port"), "80")); + } else { // Use standard HTTPS port + _params.insert(std::pair(std::string("port"), "443")); + } + } + if (!parameter_set(params::port.c_str(), _params[params::port].c_str())) { + loggers::get_instance().warning("tcp_layer::set_parameter: Unprocessed parameter: %s", params::port.c_str()); + } + it = _params.find(params::local_port); + if (it == _params.cend()) { + if (_params[params::use_ssl].compare("0") == 0) { // Use standard HTTP local_port + _params.insert(std::pair(std::string("local_port"), "80")); + } else { // Use standard HTTPS local_port + _params.insert(std::pair(std::string("local_port"), "443")); + } + } + if (!parameter_set(params::local_port.c_str(), _params[params::local_port].c_str())) { + loggers::get_instance().warning("tcp_layer::set_parameter: Unprocessed parameter: %s", params::local_port.c_str()); + } + + parameter_set(use_connection_ASPs_name(), (!server_mode) ? "yes" : "no"); + parameter_set(server_backlog_name(), "1024"); + loggers::get_instance().log("tcp_layer::tcp_layer: server_mode=%x", server_mode); + set_server_mode(server_mode); + if (server_mode) { + parameter_set("serverPort", _params[params::local_port].c_str()); + } + set_ttcn_buffer_usercontrol(false); + set_handle_half_close(true); + + map_user(); + + if (!_reconnect_on_send) { + open_client_connection(_params[params::server].c_str(), _params[params::port].c_str(), NULL, NULL); + } + } + +tcp_layer::~tcp_layer() { + loggers::get_instance().log(">>> tcp_layer::~tcp_layer: %d", _client_id); + if (_client_id != -1) { + remove_client(_client_id); + } + + unmap_user(); +} + +void tcp_layer::Handle_Fd_Event(int fd, boolean is_readable, boolean is_writable, boolean is_error) +{ + loggers::get_instance().log(">>> tcp_layer::Handle_Fd_Event: %d", fd); + Handle_Socket_Event(fd, is_readable, is_writable, is_error); + log_debug("<<< tcp_layer::Handle_Fd_Event"); +} + +void tcp_layer::Handle_Timeout(double time_since_last_call) +{ + loggers::get_instance().log(">>> tcp_layer::Handle_Timeout: %f", time_since_last_call); + Handle_Timeout_Event(time_since_last_call); + loggers::get_instance().log("<<< tcp_layer::Handle_Timeout"); +} + +void tcp_layer::send_data(OCTETSTRING& data, params& params) { + loggers::get_instance().log_msg(">>> tcp_layer::send_data: ", data); + + loggers::get_instance().log("tcp_layer::send_data: SSL mode: %x", get_ssl_use_ssl()); + + send_outgoing(static_cast(data), data.lengthof(), _client_id); +} + +void tcp_layer::receive_data(OCTETSTRING& data, params& params) { + loggers::get_instance().log_msg(">>> tcp_layer::receive_data: ", data); + + receive_to_all_layers(data, params); +} + +void tcp_layer::message_incoming(const unsigned char* message_buffer, int length, int client_id) { + loggers::get_instance().log(">>> tcp_layer::message_incoming"); + loggers::get_instance().log_to_hexa("tcp_layer::message_incoming: ", message_buffer, length); + + + float duration; + loggers::get_instance().set_start_time(_time_key); + OCTETSTRING data(length, message_buffer); + params params; + this->receive_data(data, params); // TODO Check execution time for decoding operation + loggers::get_instance().set_stop_time(_time_key, duration); +} + +void tcp_layer::client_connection_opened(int p_client_id) +{ + loggers::get_instance().log(">>> tcp_layer::client_connection_opened: %d", p_client_id); + _client_id = p_client_id; +} + +bool tcp_layer::add_user_data(int p_client_id) +{ + loggers::get_instance().log(">>> tcp_layer::add_user_data: %d", p_client_id); + if (_params[params::use_ssl].compare("0") == 0) { + loggers::get_instance().log("tcp_layer::add_user_data: Non secured mode"); + return Abstract_Socket::add_user_data(p_client_id); + } + loggers::get_instance().log("tcp_layer::add_user_data: SSL mode"); + return SSL_Socket::add_user_data(p_client_id); +} + +int tcp_layer::send_message_on_fd(int p_client_id, const unsigned char * message_buffer, int length_of_message) +{ + loggers::get_instance().log(">>> tcp_layer::send_message_on_fd: %d", p_client_id); + + if(get_user_data(p_client_id)) + { + loggers::get_instance().log("tcp_layer::send_message_on_fd: SSL mode"); + return SSL_Socket::send_message_on_fd(p_client_id, message_buffer, length_of_message); + } + + loggers::get_instance().log("tcp_layer::send_message_on_fd: Non secured mode"); + return Abstract_Socket::send_message_on_fd(p_client_id, message_buffer, length_of_message); +} + +int tcp_layer::send_message_on_nonblocking_fd(int p_client_id, const unsigned char * message_buffer, int length_of_message) +{ + loggers::get_instance().log(">>> tcp_layer::send_message_on_nonblocking_fd: %d", p_client_id); + + if(get_user_data(p_client_id)) + { + loggers::get_instance().log("tcp_layer::send_message_on_nonblocking_fd: SSL mode"); + return SSL_Socket::send_message_on_nonblocking_fd(p_client_id, message_buffer, length_of_message); + } + + loggers::get_instance().log("tcp_layer::send_message_on_nonblocking_fd: Non secured mode"); + return Abstract_Socket::send_message_on_nonblocking_fd(p_client_id, message_buffer, length_of_message); +} + +int tcp_layer::receive_message_on_fd(int p_client_id) +{ + loggers::get_instance().log(">>> tcp_layer::receive_message_on_fd: %d", p_client_id); + + if(get_user_data(p_client_id)) { + // INFO: it is assumed that only SSL_Socket assigns user data to each peer + loggers::get_instance().log("tcp_layer::receive_message_on_fd: SSL mode"); + return SSL_Socket::receive_message_on_fd(p_client_id); + } + + loggers::get_instance().log("tcp_layer::receive_message_on_fd: Non secured mode"); + return Abstract_Socket::receive_message_on_fd(p_client_id); +} + +void tcp_layer::peer_disconnected(int p_client_id) +{ + loggers::get_instance().log(">>> tcp_layer::peer_disconnected: %d", p_client_id); + + Abstract_Socket::peer_disconnected(p_client_id); + _client_id = -1; +} + +tcp_layer_factory tcp_layer_factory::_f; + diff --git a/ccsrc/Protocols/Tcp/tcp_layer.hh b/ccsrc/Protocols/Tcp/tcp_layer.hh new file mode 100644 index 0000000..166a145 --- /dev/null +++ b/ccsrc/Protocols/Tcp/tcp_layer.hh @@ -0,0 +1,99 @@ +/*! + * \file tcp_layer.hh + * \brief Header file for ITS TCP socket based protocol port layer. + * \author ETSI STF525 + * \copyright ETSI Copyright Notification + * No part may be reproduced except as authorized by written permission. + * The copyright and the foregoing restriction extend to reproduction in all media. + * All rights reserved. + * \version 0.1 + */ +#pragma once + +#include "layer.hh" + +#include "Abstract_Socket.hh" + +class PORT; //! Forward declaration of TITAN class + +/*! + * \class tcp_layer + * \brief This class provides description of ITS TCP port protocol layer + */ +class tcp_layer : public layer, public SSL_Socket, public PORT { + params _params; //! Layer parameters + int _client_id; //! Connection identifier + std::string _time_key; //! \todo + bool _reconnect_on_send; //! Set to true if connection shall be done when sending data. Otherwise, connection is established by the \see constructor + +public: //! \publicsection + /*! + * \brief Specialised constructor + * Create a new instance of the tcp_layer class + * \param[in] p_type \todo + * \param[in] p_param \todo + */ + tcp_layer(const std::string& p_type, const std::string& p_param); + /*! + * \brief Default destructor + * \remark If \see _reconnect_on_send is set to false, the disconnection is done by the destructor + */ + virtual ~tcp_layer(); + + /*! + * \virtual + * \fn void send_data(OCTETSTRING& data, params& params); + * \brief Send bytes formated data to the lower layers + * \param[in] p_data The data to be sent + * \param[in] p_params Some parameters to overwrite default value of the lower layers parameters + * \virtual + */ + virtual void send_data(OCTETSTRING& data, params& params); + /*! + * \virtual + * \fn void receive_data(OCTETSTRING& data, params& params); + * \brief Receive bytes formated data from the lower layers + * \param[in] p_data The bytes formated data received + * \param[in] p_params Some lower layers parameters values when data was received + */ + virtual void receive_data(OCTETSTRING& data, params& info); + + /*! + * \virtual + * \fn void message_incoming(const unsigned char* message_buffer, int length, int client_id = -1); + * \brief Receive bytes formated data from the lower layers + * \param[in] p_buffer The bytes formated data received + * \param[in] p_length The number of bytes received + * \param[in] p_client_id The connection identifier.Default: -1 + */ + virtual void message_incoming(const unsigned char* p_buffer, int p_length, int p_client_id = -1); + +protected: //! \protectedsection + void Add_Fd_Read_Handler(int fd) { Handler_Add_Fd_Read(fd); }; + void Add_Fd_Write_Handler(int fd) { Handler_Add_Fd_Write(fd); }; + void Remove_Fd_Read_Handler(int fd) { Handler_Remove_Fd_Read(fd); }; + void Remove_Fd_Write_Handler(int fd) { Handler_Remove_Fd_Write(fd); }; + void Remove_Fd_All_Handlers(int fd) { Handler_Remove_Fd(fd); }; + void Handler_Uninstall() { Uninstall_Handler(); } + void Timer_Set_Handler(double call_interval, boolean is_timeout = TRUE, + boolean call_anyway = TRUE, boolean is_periodic = TRUE) { + Handler_Set_Timer(call_interval, is_timeout, call_anyway, is_periodic); + }; + + const char* remote_address_name() { return params::server.c_str(); }; + const char* remote_port_name() { return params::port.c_str(); }; + const char* socket_debugging_name() { return params::debug.c_str(); }; + const char* ssl_use_ssl_name() { return params::use_ssl.c_str(); }; + + void client_connection_opened(int p_client_id); + bool add_user_data(int p_client_id); + int send_message_on_fd(int p_client_id, const unsigned char * message_buffer, int length_of_message); + int send_message_on_nonblocking_fd(int client_id, const unsigned char * message_buffer, int length_of_message); + int receive_message_on_fd(int p_client_id); + void peer_disconnected(int p_client_id); + +private: //! \privatesection + void Handle_Fd_Event(int fd, boolean is_readable, boolean is_writable, boolean is_error); + void Handle_Timeout(double time_since_last_call); +}; // End of class tcp_layer + diff --git a/ccsrc/Protocols/Tcp/tcp_layer_factory.hh b/ccsrc/Protocols/Tcp/tcp_layer_factory.hh new file mode 100644 index 0000000..47a1613 --- /dev/null +++ b/ccsrc/Protocols/Tcp/tcp_layer_factory.hh @@ -0,0 +1,44 @@ +/*! + * \file tcp_layer_factory.hh + * \brief Header file for ITS TCP socket based protocol layer factory. + * \author ETSI STF525 + * \copyright ETSI Copyright Notification + * No part may be reproduced except as authorized by written permission. + * The copyright and the foregoing restriction extend to reproduction in all media. + * All rights reserved. + * \version 0.1 + */ +#pragma once + +#include "layer_stack_builder.hh" + +#include "tcp_layer.hh" + +/*! + * \class tcp_layer_factory + * \brief This class provides a factory class to create an tcp_layer class instance + */ +class tcp_layer_factory : public layer_factory { + static tcp_layer_factory _f; //! Reference to the unique instance of this class +public: //! \publicsection + /*! + * \brief Default constructor + * Create a new instance of the tcp_layer class + * \remark The TCP socket based layer identifier is TCP + */ + tcp_layer_factory() { + // Register factory + layer_stack_builder::register_layer_factory("TCP", this); + }; + /*! + * \fn layer* create_layer(const std::string & type, const std::string & param); + * \brief Create the layers stack based on the provided layers stack description + * \param[in] p_type The provided layers stack description + * \param[in] p_params Optional parameters + * \return 0 on success, -1 otherwise + */ + inline virtual layer* create_layer(const std::string& p_type, const std::string& p_param){ + return new tcp_layer(p_type, p_param); + }; +}; // End of class tcp_layer_factory + diff --git a/docker/Dockerfile b/docker/Dockerfile new file mode 100644 index 0000000..353da08 --- /dev/null +++ b/docker/Dockerfile @@ -0,0 +1,131 @@ +FROM ubuntu:18.04 + +MAINTAINER ETSI STF 549 + +LABEL description="STF549 Docker Image" + +ENV TERM=xterm +ENV HOSTNAME docker-titan-STF549 + +RUN DEBIAN_FRONTEND=noninteractive apt-get update \ + && DEBIAN_FRONTEND=noninteractive apt-get install software-properties-common -y \ + && DEBIAN_FRONTEND=noninteractive add-apt-repository ppa:linuxuprising/java -y + +RUN DEBIAN_FRONTEND=noninteractive apt-get update \ + && DEBIAN_FRONTEND=noninteractive apt-get --allow-unauthenticated install -y \ + autoconf \ + bison \ + build-essential \ + cmake \ + curl \ + dos2unix \ + doxygen \ + emacs \ + expect \ + flex \ + g++-8 \ + gcc-8 \ + gdb \ + git-core \ + gnutls-bin \ + graphviz \ + inetutils-ping \ + libglib2.0-dev \ + libpcap-dev \ + libgcrypt-dev \ + libncurses5-dev \ + libssl-dev \ + libtool-bin \ + libtool \ + libwireshark-dev \ + libxml2-dev \ + lsof \ + ntp \ + openssh-server \ + pkg-config \ + qt5-default \ + qttools5-dev \ + qtmultimedia5-dev \ + libqt5svg5-dev \ + subversion \ + sudo \ + sshpass \ + tcpdump \ + texlive-font-utils \ + tshark \ + tzdata \ + valgrind \ + vim \ + vsftpd \ + xutils-dev \ + tree \ + unzip \ + wget \ + xsltproc \ + && DEBIAN_FRONTEND=noninteractive apt-get autoremove --purge -y \ + && DEBIAN_FRONTEND=noninteractive apt-get autoclean \ + && rm -rf /var/lib/apt/lists/* + +RUN echo "docker-titan-STF549" > /etc/hostname \ + && echo "root:etsi" | chpasswd + +RUN useradd --create-home --shell /bin/bash --user-group etsi --groups sudo \ + && echo "etsi:etsi" | chpasswd \ + && adduser etsi sudo + +RUN cd /home/etsi \ + && echo "" >> /home/etsi/.bashrc \ + && echo "export HOME=/home/etsi" >> /home/etsi/.bashrc \ + && echo "export LD_LIBRARY_PATH=/home/etsi/dev/etsi_emcom/lib:$LD_LIBRARY_PATH" >> /home/etsi/.bashrc \ + && echo "export PATH=/home/etsi/bin:$PATH" >> /home/etsi/.bashrc \ + && echo "cd /home/etsi" >> /home/etsi/.bashrc \ + && echo ". ./devenv.bash" >> /home/etsi/.bashrc \ + && . /home/etsi/.bashrc \ + && mkdir -p bin lib include tmp frameworks docs man dev \ + && chown etsi:etsi bin lib include tmp frameworks docs man dev \ + && echo "etsi ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers + +# Seems that COPY does not work as expected, to be investaged +#==> Fallback to more secured solution +#COPY [^.]* /home/etsi/dev/STF549_Ng112/ +#COPY home home/etsi +RUN git clone https://forge.etsi.org/gitlab/emergency-communications/NG112.git /home/etsi/dev/STF549_Ng112 +RUN chown -R etsi /home/etsi/dev/STF549_Ng112/ && cd /home/etsi/dev/STF549_Ng112 + +USER etsi + +RUN cd /home/etsi/frameworks \ + && git clone https://git.savannah.gnu.org/git/osip.git ./osip \ + && cd osip \ + && ./autogen.sh \ + && ./configure --prefix=/home/etsi \ + && make && make install + +RUN cd /home/etsi/frameworks \ + && wget -q 'http://ftp.halifax.rwth-aachen.de/eclipse//technology/epp/downloads/release/oxygen/2/eclipse-cpp-oxygen-2-linux-gtk-x86_64.tar.gz' -Oeclipse-cpp-oxygen-2-linux-gtk-x86_64.tar.gz \ + && tar -zxvf ./eclipse-cpp-oxygen-2-linux-gtk-x86_64.tar.gz \ + && rm -f ./eclipse-cpp-oxygen-2-linux-gtk-x86_64.tar.gz \ + && cd /home/etsi/dev/STF549_Ng112 \ + && cd /home/etsi/dev/STF549_Ng112/ttcn \ + && git clone https://forge.etsi.org/gitlab/LIBS/LibSip.git ./LibSip \ + && git clone https://forge.etsi.org/gitlab/LIBS/LibIms.git ./LibIms \ + && git clone https://forge.etsi.org/gitlab/LIBS/LibCommon.git ./LibCommon \ + && git clone -bSTF525 https://forge.etsi.org/gitlab/LIBS/LibIts ./LibIts \ + && cd /home/etsi/dev/STF549_Ng112/ttcn/LibIts \ + && rm -fr asn1 t3q xsd \ + && cd ttcn && rm -fr BTP CALM CAM Common DCC DENM GeoNetworking Ipv6OverGeoNetworking IVIM MapemSpatem Pki Security SremSsem V2G \ + && cd /home/etsi/dev/STF549_Ng112/scripts \ + && chmod 775 *.bash devenv.bash.* \ + && cd /home/etsi \ + && ln -sf /home/etsi/dev/STF549_Ng112/scripts/devenv.bash.ubuntu /home/etsi/devenv.bash \ + && ls -ltr /home/etsi \ + && export HOME=/home/etsi \ + && . /home/etsi/devenv.bash \ + && cd /home/etsi/dev/STF549_Ng112/scripts \ + && ./build_titan.bash \ + && . /home/etsi/devenv.bash \ + && ./update_emcom_project.bash \ + && cd /home/etsi/dev/etsi_emcom/src/AtsNg112/objs \ + && ../bin/ng112_generate_makefile.bash + +# That's all Floks diff --git a/docker/build-container.sh b/docker/build-container.sh new file mode 100755 index 0000000..1886049 --- /dev/null +++ b/docker/build-container.sh @@ -0,0 +1,30 @@ +#!/bin/bash +# Copyright ETSI 2018 +# See: https://forge.etsi.org/etsi-forge-copyright-statement.txt + +#set -e +#set -vx + +DOCKER_FILE=./docker/Dockerfile +if [ -f ${DOCKER_FILE} ] +then + docker rm --force stf549_ng112 + docker build --tag stf549_ng112 --force-rm -f ${DOCKER_FILE} . + if [ "$?" != "0" ] + then + echo "Docker build failed: $?" + exit -1 + fi + docker image ls -a + docker inspect stf549_ng112:latest + if [ "$?" != "0" ] + then + echo "Docker inspect failed: $?" + exit -2 + fi +else + exit -3 +fi + +# That's all Floks +exit 0 diff --git a/docker/run-container.sh b/docker/run-container.sh new file mode 100755 index 0000000..383d9fa --- /dev/null +++ b/docker/run-container.sh @@ -0,0 +1,15 @@ +#!/bin/bash +# Copyright ETSI 2018 +# See: https://forge.etsi.org/etsi-forge-copyright-statement.txt + +#set -e +#set -vx + +docker run stf549_ng112:latest "/bin/bash" \ + -c "source /home/etsi/devenv.bash \ + && cd /home/etsi/dev/etsi_emcom/src/AtsNg112/objs \ + && ../bin/run_all.bash \ + && ls -ltr ../logs" + +# That's all Floks +exit 0 diff --git a/docker/validate-in-docker.sh b/docker/validate-in-docker.sh new file mode 100755 index 0000000..e69de29 diff --git a/docs/o2.cfg b/docs/o2.cfg new file mode 100644 index 0000000..e69de29 diff --git a/etc/AtsMec/AtsMec_LocationAPI.cfg b/etc/AtsMec/AtsMec_LocationAPI.cfg new file mode 100644 index 0000000..3f7a71b --- /dev/null +++ b/etc/AtsMec/AtsMec_LocationAPI.cfg @@ -0,0 +1,71 @@ +[MODULE_PARAMETERS] +# This section shall contain the values of all parameters that are defined in your TTCN-3 modules. + +# IUT roles + +LibCommon_Time.PX_TAC := 30.0 +LibCommon_Sync.PX_TSYNC_TIME_LIMIT := 30.0; +LibCommon_Sync.PX_TSHUT_DOWN_TIME_LIMIT := 30.0; + +LibItsHttp_Pics.PICS_HEADER_HOST := "192.168.0.5" + +LibItsHttp_Pics.PICS_HEADER_CONTENT_TYPE := "application/json;charset=utf-8" + +LibMec_Pics.PICS_ME_APP_IUT := true + +[LOGGING] +# In this section you can specify the name of the log file and the classes of events +# you want to log into the file or display on console (standard error). + +LogFile := "../logs/%e.%h-%r.%s" +FileMask := LOG_ALL | USER | DEBUG | MATCHING +ConsoleMask := LOG_ALL | USER | DEBUG | MATCHING +#FileMask := ERROR | WARNING | USER | MATCHING | EXECUTOR_RUNTIME | VERDICTOP +#ConsoleMask := ERROR | WARNING | USER | MATCHING | EXECUTOR_RUNTIME | VERDICTOP +LogSourceInfo := Stack +LogEntityName:= Yes +LogEventTypes:= Yes +#TimeStampFormat := DateTime + +[TESTPORT_PARAMETERS] +# In this section you can specify parameters that are passed to Test Ports. +system.httpPort.params := "HTTP(codecs=json:json_codec)/TCP(debug=1,server=192.168.0.5,use_ssl=0)" + +[DEFINE] +# In this section you can create macro definitions, +# that can be used in other configuration file sections except [INCLUDE] and [ORDERED_INCLUDE]. + +[INCLUDE] +# To use configuration settings given in other configuration files, +# the configuration files just need to be listed in this section, with their full or relative pathnames. + +[ORDERED_INCLUDE] +# To use configuration settings given in other configuration files, +# the configuration files just need to be listed in this section, with their full or relative pathnames. + +[EXTERNAL_COMMANDS] +# This section can define external commands (shell scripts) to be executed by the ETS +# whenever a control part or test case is started or terminated. + +#BeginTestCase := "" +#EndTestCase := "" +#BeginControlPart := "" +#EndControlPart := "" + +[EXECUTE] +# In this section you can specify what parts of your test suite you want to execute. +AtsMec_TestControl.control + +[GROUPS] +# In this section you can specify groups of hosts. These groups can be used inside the +# [COMPONENTS] section to restrict the creation of certain PTCs to a given set of hosts. + +[COMPONENTS] +# This section consists of rules restricting the location of created PTCs. + +[MAIN_CONTROLLER] +# The options herein control the behavior of MC. +KillTimer := 10.0 +LocalAddress := 127.0.0.1 +TCPPort := 12000 +NumHCs := 1 diff --git a/etc/TestCodec/TestCodec.cfg b/etc/TestCodec/TestCodec.cfg new file mode 100644 index 0000000..1f4d1bb --- /dev/null +++ b/etc/TestCodec/TestCodec.cfg @@ -0,0 +1,69 @@ +[MODULE_PARAMETERS] +# This section shall contain the values of all parameters that are defined in your TTCN-3 modules. + +#LibItsHttp_Pics.PICS_HEADER_HOST := "httpbin.org" +LibItsHttp_Pics.PICS_HEADER_HOST := "ptsv2.com" +#LibItsHttp_Pics.PICS_HEADER_CONTENT_TYPE := "application/held+xml;charset=utf-8"; + +LibCommon_Time.PX_TAC := 30.0 + +[LOGGING] +# In this section you can specify the name of the log file and the classes of events +# you want to log into the file or display on console (standard error). + +LogFile := "../logs/%e.%h-%r.%s" +FileMask := LOG_ALL | USER | DEBUG | MATCHING | DEBUG_ENCDEC +ConsoleMask := LOG_ALL | USER | DEBUG | MATCHING | DEBUG_ENCDEC +#FileMask := ERROR | WARNING | USER | MATCHING | EXECUTOR_RUNTIME | VERDICTOP +#ConsoleMask := ERROR | WARNING | USER | MATCHING | EXECUTOR_RUNTIME | VERDICTOP +LogSourceInfo := Stack +LogEntityName:= Yes +LogEventTypes:= Yes +#TimeStampFormat := DateTime + +[TESTPORT_PARAMETERS] +# In this section you can specify parameters that are passed to Test Ports. +#system.httpPort.params := "HTTP(codecs=held:held_codec;html:html_codec;json:json_codec)/TCP(debug=1,server=ptsv2.com,port=80,use_ssl=0)" +# For manual testing, use this command: openssl s_client -connect nghttp2.org:443 -msg +#system.httpPort.params := "HTTP(codecs=held:held_codec;html:html_codec;json:json_codec)/TCP(debug=1,server=nghttp2.org,port=443,use_ssl=1)" + +#system.SIPP.params := "SIP/UDP_PCAP(dst_ip=192.168.1.250,dst_port=5060,src_ip=192.168.1.253,src_port=5060)/ETH(mac_src=080027d2b658,mac_dst=90fd61e61902,eth_type=0800)/PCAP(mac_src=080027d2b658,nic=eth1,filter=and udp port 12345)" + +[DEFINE] +# In this section you can create macro definitions, +# that can be used in other configuration file sections except [INCLUDE] and [ORDERED_INCLUDE]. + +[INCLUDE] +# To use configuration settings given in other configuration files, +# the configuration files just need to be listed in this section, with their full or relative pathnames. + +[ORDERED_INCLUDE] +# To use configuration settings given in other configuration files, +# the configuration files just need to be listed in this section, with their full or relative pathnames. + +[EXTERNAL_COMMANDS] +# This section can define external commands (shell scripts) to be executed by the ETS +# whenever a control part or test case is started or terminated. + +#BeginTestCase := "" +#EndTestCase := "" +#BeginControlPart := "" +#EndControlPart := "" + +[EXECUTE] +# In this section you can specify what parts of your test suite you want to execute. +TestCodec_External.tc_encode_LocationInfo + +[GROUPS] +# In this section you can specify groups of hosts. These groups can be used inside the +# [COMPONENTS] section to restrict the creation of certain PTCs to a given set of hosts. + +[COMPONENTS] +# This section consists of rules restricting the location of created PTCs. + +[MAIN_CONTROLLER] +# The options herein control the behavior of MC. +KillTimer := 10.0 +LocalAddress := 127.0.0.1 +TCPPort := 12000 +NumHCs := 1 diff --git a/scripts/build_titan.bash b/scripts/build_titan.bash new file mode 100755 index 0000000..7305264 --- /dev/null +++ b/scripts/build_titan.bash @@ -0,0 +1,103 @@ +#!/bin/bash +# Copyright ETSI 2018 +# See: https://forge.etsi.org/etsi-forge-copyright-statement.txt + +set -e +#set -vx + +clear + +if [ -z "${TOP}" ] +then + echo "Failed, TOP variable not defined, exit" + exit 1 +fi + +CURDIR=`pwd` +TITAN_DIR=${TOP}/.. + +# Move to the right directory +if [ ! -d ${TITAN_DIR} ] +then + echo "Titan directory does not exist, create it" + # Create TITAN directories + mkdir -p ${TITAN_DIR} + if [ ! "$?" -eq "0" ] + then + echo "Failed, TOP variable not defined, exit" + exit 2 + fi + cd ${TITAN_DIR} + # Clone all TITAN repositories + if [ ! -f ${CURDIR}/titan_repos.txt ] + then + echo "${HOME_BIN}/titan_repos.txt file does not exist, exit" + rm -fr ${TOP} + rm -fr ${TOP}/.. + exit 3 + fi + TITAN_REPOS=`cat ${CURDIR}/titan_repos.txt` + for i in ${TITAN_REPOS}; + do + git clone $i + if [ ! "$?" -eq "0" ] + then + echo "Failed to clone $i, exit" + exit 4 + fi + done +else + cd ${TITAN_DIR} + # Update github folders + DIRS=`find . -type d -name ".git" -exec dirname {} \;` + for i in ${DIRS}; + do + echo "Processing $i..." + cd $i + git fetch + if [ ! "$?" -eq "0" ] + then + echo "Failed to fetch $i, continue" + else + git pull + if [ ! "$?" -eq "0" ] + then + echo "Failed to pull $i, continue" + fi + fi + cd - + done +fi + +# Build TITAN core +export JNI=no +export GUI=no +export DEBUG=no +export GEN_PDF=no +if [ -d ${TTCN3_DIR} ] +then + rm -fr ${TTCN3_DIR} +fi +mkdir ${TTCN3_DIR} +cd ./titan.core +/bin/cat < Makefile.personal +JNI:=no +GUI:=no +DEBUG:=no +GEN_PDF:=no +EOF + +echo "Starting build..." +make clean +if [ "${OSTYPE}" == "cygwin" ] +then + make -j +else + make +fi +make install +echo "Build done" + +# Go back to initial directory +cd ${CURDIR} +exit 0 diff --git a/scripts/devenv.bash.ubuntu b/scripts/devenv.bash.ubuntu new file mode 100755 index 0000000..30ae0f2 --- /dev/null +++ b/scripts/devenv.bash.ubuntu @@ -0,0 +1,69 @@ +#!/bin/bash + +# Turn on debug mode +#set -vx + +set -e + +# Colors and Prompt +#export PS1="\w\$ " +#export PS1="\D{%Y-%m-%d %H:%M:%S} \w\n\$ " + +export TERM=xterm +export EDITOR=emacs +set -o emacs + +# Home working directories +export HOME_FRAMEWORKS=${HOME}/frameworks +export HOME_LIB=${HOME}/lib +export HOME_BIN=${HOME}/bin +export HOME_ETC=${HOME}/etc +export HOME_TMP=${HOME}/tmp +export HOME_DOCS=${HOME}/docs + +# Home common include directory +export HOME_INC=${HOME}/include + +# Update PATH environment variable +export PATH=${HOME_BIN}:${PATH} + +# Update LD_LIBRARY_PATH environment variable +if [ "${LD_LIBRARY_PATH}" == "" ] +then + export LD_LIBRARY_PATH=${HOME_LIB}:/usr/local/lib +else + export LD_LIBRARY_PATH=${HOME_LIB}:/usr/local/lib:${LD_LIBRARY_PATH} +fi + + +# Add JAVA support +export JAVA_VERSION=1.8.0_92 +export JAVA_JDK=jdk${JAVA_VERSION} +if [ -d "${HOME_FRAMEWORKS}/${JAVA_JDK}" ] +then + export JAVA_HOME=${HOME_FRAMEWORKS}/${JAVA_JDK}/bin +else + unset JAVA_VERSION + unset JAVA_JDK +fi + +# Add TITAN support +export TOP=${HOME_FRAMEWORKS}/titan/titan.core +export TTCN3_DIR=${TOP}/Install +if [ -d ${TTCN3_DIR} ] +then + export TTCN3_BROWSER=mozilla + export PATH=${TTCN3_DIR}/bin:${PATH} + export LD_LIBRARY_PATH=${TTCN3_DIR}/lib:${LD_LIBRARY_PATH} + export MANPATH=${TTCN3_DIR}/man:${MANPATH} + export TTCN3_LICENSE_FILE=${TTCN3_DIR}/etc/license/license.dat + export PATH_DEV_TTCN=${HOME}/dev/ttcn3 + # ITS support + export PATH_DEV_ITS=${HOME}/dev/etsi_its + # Emergency Communication support + export PATH_DEV_EMCOM=${HOME}/dev/etsi_emcom + # Validation folder + export VALIDATION_DIR=${HOME} +fi + +export BROWSER=netsurf diff --git a/scripts/mec_generate_makefile.bash b/scripts/mec_generate_makefile.bash new file mode 100755 index 0000000..9db4c05 --- /dev/null +++ b/scripts/mec_generate_makefile.bash @@ -0,0 +1,235 @@ +#!/bin/bash +#set -e +set -vx + +function f_exit { + cd ${CURPWD} + + unset TTCN_FILES + unset CC_FILES + unset CFG_FILES + unset EXECUTABLE + echo $1 + exit $2 +} + +function f_usage { + echo "build.bash: This script import from External Disk the " + echo "Optional arguments:" + echo " prof: Generate a makefile including profiling options (e.g. ./build.bash prof)" + exit 0 +} + +clear + +if [ "$1" == "help" ] +then + f_usage +fi + +ATS_NAME=Mec + +#CURPWD=`pwd` +if [ ! "${PWD##*/}" == "objs" ] +then + cd ../objs + if [ ! $? == 0 ] + then + echo "Please move to PROJECT/obj directory" + exit 1 + fi +fi +# Remove everything +rm -fr ../objs/*.hh +rm -fr ../objs/*.cc +rm -fr ../objs/*.log +rm -fr ../objs/*.o +rm -fr ../objs/Makefile + +# Remove useless files +find .. -type f -name "*~" -exec rm {} \; +find .. -type f -name "*.bak" -exec rm {} \; +find .. -type f -name "*.log" -exec rm {} \; + +# Build JSON files if any and put them in objs directory +#JSON_PATH=${PATH_DEV_MEC}/src/LibSip/json +JSON_PATH=${PATH_DEV_MEC}/null # Do not use json2ttcn for now, need to fix issues first +if [ -d ${JSON_PATH} ] +then + JSON_FILES=`find ${JSON_PATH} -name '*.json'` + + if [ "${OSTYPE}" == "cygwin" ] + then + json2ttcn.exe ${JSON_FILES} + else + json2ttcn ${JSON_FILES} + fi + if [ "$?" != "0" ] + then + f_exit "Failed to generate JSON source code" 2 + fi + JSON_FILES=`find . -name '*.ttcn'` +fi + +REFERENCES="LibCommon LibHttp LibMec LibMec/LocationAPI" +for i in ${REFERENCES} +do + # TTCN code + for j in `find ${PATH_DEV_MEC}/src/$i/ttcn -type f -name "*.ttcn"`; + do + ln -sf $j ../ttcn/`basename $j` + done + # Include source code + files=`find ${PATH_DEV_MEC}/src/$i/include -type f` + if [ "${files}" != " " ] + then + for j in ${files}; + do + ln -sf $j ../include/`basename $j` + done + fi + # CC source code + files=`find ${PATH_DEV_MEC}/src/$i/src -type f` + if [ "${files}" != " " ] + then + for j in ${files}; + do + ln -sf $j ../src/`basename $j` + done + fi +done + +# Generate the list of the TTCN-3 files +TTCN_FILES=`find .. -name '*.ttcn*'` + +# Start ATS generation - Step 1 +if [ "${OSTYPE}" == "cygwin" ] +then + rm ../bin/*.exe ../lib/*.dll + compiler.exe -b -e -f -g -l -L -M -n -O -t -r -R -U none -X ${TTCN_FILES} 2>&1 3>&1 | tee build.log + if [ "$?" == "1" ] + then + f_exit "Failed to compile ATS" 4 + fi +else + compiler -b -e -f -g -l -L -M -n -O -t -r -R -U none -X ${TTCN_FILES} 2>&1 3>&1 | tee build.log + if [ "$?" == "1" ] + then + f_exit "Failed to generate ATS source code" 6 + fi +fi + +# Sart ATS generation - Step 2 +# Create working variables +CC_FILES=`find ../src -name '*.c*'` +FWK_FILES=`find ${PATH_DEV_MEC}/framework/ -name '*.c*'` +CFG_FILES=`find ../etc -name '*.cfg'` + +# Sart ATS generation - Step 3 +if [ "${OSTYPE}" == "cygwin" ] +then + ttcn3_makefilegen.exe -d -f -g -m -M -R -U none -e Ats${ATS_NAME} ${TTCN_FILES} ${CC_FILES} ${FWK_FILES} ${CFG_FILES} | tee --append build.log + if [ "$?" == "1" ] + then + f_exit "Failed to compile ATS" 5 + fi +else + ttcn3_makefilegen -d -f -g -m -M -R -U none -e Ats${ATS_NAME} ${TTCN_FILES} ${CC_FILES} ${FWK_FILES} ${CFG_FILES} | tee --append build.log + if [ "$?" == "1" ] + then + f_exit "Failed to generate ATS source code" 7 + fi +fi + +# Remove port skeletons to use src/ +for i in `ls ../include/*.hh` +do + if [ -f ./`basename $i` ] + then + rm ./`basename $i` + fi +done +for i in `ls ../src/*.cc` +do + if [ -f ./`basename $i` ] + then + rm ./`basename $i` + fi +done + +# Check if Makefile was generated +if [ ! -f ./Makefile ] +then + f_exit "Failed to generate ATS source code" 8 +fi + +# Patch ATS generated files +#./bin/patch.bash 2>&1 3>&1 | tee --append build.log +# Add compiler/linker options +# -DASN_DISABLE_OER_SUPPORT is required for CAMCodec and DENMCodec +if [ "$1" == "prof" ] +then + if [ "${OSTYPE}" == "cygwin" ] + then + CXXFLAGS_DEBUG_MODE='s/-Wall/-pg -Wall -std=c++11 -fPIC -D_XOPEN_SOURCE=700 -DAS_USE_SSL -DENABLE_TRACE -pthreads -fstack-check -fstack-protector -fsanitize=leak -fsanitize=address -fno-omit-frame-pointer/g' + else + CXXFLAGS_DEBUG_MODE='s/-Wall/-pg -Wall -std=c++11 -fPIC -DAS_USE_SSL -DENABLE_TRACE -pthreads -fstack-check -fstack-protector -fsanitize=leak -fsanitize=address -fno-omit-frame-pointer/g' + fi + LDFLAGS_DEBUG_MODE='s/LDFLAGS = /LDFLAGS = -pg -pthread -fPIC -fstack-check -fstack-protector -fsanitize=leak -fsanitize=address -fno-omit-frame-pointer/g' +else + if [ "${OSTYPE}" == "cygwin" ] + then + CXXFLAGS_DEBUG_MODE='s/-Wall/-ggdb -O0 -Wall -std=c++11 -fPIC -DAS_USE_SSL -DENABLE_TRACE -D_XOPEN_SOURCE=700 -pthread -fstack-check -fstack-protector -fsanitize=leak -fsanitize=address -fno-omit-frame-pointer/g' + else + CXXFLAGS_DEBUG_MODE='s/-Wall/-ggdb -O0 -Wall -std=c++11 -fPIC -DAS_USE_SSL -DENABLE_TRACE -pthread -fstack-check -fstack-protector -fsanitize=leak -fsanitize=address -fno-omit-frame-pointer/g' + fi + LDFLAGS_DEBUG_MODE='s/LDFLAGS = /LDFLAGS = -g -pthread -fPIC -fstack-check -fstack-protector -fsanitize=leak -fsanitize=address -fno-omit-frame-pointer/g' +fi +ADD_INCLUDE='/CPPFLAGS = /a\\CPPFLAGS += -I/usr/local/share -I$(PATH_DEV_MEC)/include -I$(PATH_DEV_MEC)/framework/include -I../include -I../../LibEmcom/Common/include -I../../LibEmcom/LibMec/include -I$(HOME_FRAMEWORKS)/osip/include -I$(HOME_INC) -I.' +ADD_LIBRARIES='s/LINUX_LIBS = -lxml2/LINUX_LIBS = -lrt -lxml2 -lpcap -lstdc++fs -lssl -L\$\(HOME_FRAMEWORKS\)\/osip\/src\/osipparser2\/\.libs -losipparser2/g' +sed --in-place "${CXXFLAGS_DEBUG_MODE}" ./Makefile +sed --in-place "${LDFLAGS_DEBUG_MODE}" ./Makefile +sed --in-place "${ADD_INCLUDE}" ./Makefile +sed --in-place "${ADD_LIBRARIES}" ./Makefile +# Update COMPILER_FLAGS +COMPILER_FLAGS='s/COMPILER_FLAGS = /COMPILER_FLAGS = -e -O /g' +sed --in-place "${COMPILER_FLAGS}" ./Makefile +# Update clean clause +CLEAN_LINE='s/$(RM) $(EXECUTABLE)/$(RM) ..\/bin\/$(EXECUTABLE) ..\/src\/*.o/g' +sed --in-place "${CLEAN_LINE}" ./Makefile +# Move binary file command +EXECUTABLE=MyExample +MV_CMD='s/all: $(TARGET) ;/all: $(TARGET) ; @if [ -f ..\/objs\/$(EXECUTABLE) ]; then mv ..\/objs\/$(EXECUTABLE) ..\/bin; fi ;/g' +sed --in-place "${MV_CMD}" ./Makefile +# Add run command +ADD_HOST='/PLATFORM = /aHOST=127.0.0.1' +ADD_PORT='/PLATFORM = /aPORT=12000' +sed --in-place "${ADD_PORT}" ./Makefile +sed --in-place "${ADD_HOST}" ./Makefile +ADD_RUN_LINE_1='$arun: all' +ADD_RUN_LINE_2='$a\\t@sudo LD_LIBRARY_PATH=$(LD_LIBRARY_PATH) $(PWD)/../bin/$(EXECUTABLE) $(HOST) $(PORT)' +sed --in-place "${ADD_RUN_LINE_1}" ./Makefile +sed --in-place "${ADD_RUN_LINE_2}" ./Makefile +ADD_RUN_LINE_1='$arun_d: all' +ADD_RUN_LINE_2='$a\\t@gdb --args $(PWD)/../bin/$(EXECUTABLE) $(HOST) $(PORT)' +sed --in-place "${ADD_RUN_LINE_1}" ./Makefile +sed --in-place "${ADD_RUN_LINE_2}" ./Makefile +ADD_RUN_LINE_1='$arun_v: all' +ADD_RUN_LINE_2='$a\\t@sudo LD_LIBRARY_PATH=$(LD_LIBRARY_PATH) valgrind -v --tool=memcheck --leak-check=yes --show-reachable=yes --track-fds=yes --run-cxx-freeres=yes $(PWD)/../bin/$(EXECUTABLE) $(HOST) $(PORT)' +sed --in-place "${ADD_RUN_LINE_1}" ./Makefile +sed --in-place "${ADD_RUN_LINE_2}" ./Makefile +# Add gendoc entry +ADD_RUN_LINE_1='$agendoc: ../docs/o2.cfg' +ADD_RUN_LINE_2='$a\\tdoxygen ../docs/o2.cfg' +sed --in-place "${ADD_RUN_LINE_1}" ./Makefile +sed --in-place "${ADD_RUN_LINE_2}" ./Makefile + +# Build all +make all 2>&1 3>&1 | tee --append build.log +if [ "$?" == "1" ] +then + f_exit "Failed to generate ATS source code" 9 +fi +export LD_LIBRARY_PATH=~/frameworks/osip/src/osipparser2/.libs:$LD_LIBRARY_PATH +../bin/Ats${ATS_NAME} -v +f_exit "Build done successfully" 0 diff --git a/scripts/merge_mec_project.bash b/scripts/merge_mec_project.bash new file mode 100755 index 0000000..a82229c --- /dev/null +++ b/scripts/merge_mec_project.bash @@ -0,0 +1,240 @@ +#!/bin/bash + +# Debug mode +#set -e +#set -vx + +# Usage: sudo ./merge_mec_project.bash +# TODO Use git clone in temporary directory + +OLDPWD=`pwd` + +# Storing path +VAGRANT_DIR=~/tmp +if [ ! -d ${VAGRANT_DIR} ] +then + exit -1 +else + VAGRANT_DIR=${VAGRANT_DIR}/to_be_merged + if [ -d ${VAGRANT_DIR} ] + then + rm -f ${VAGRANT_DIR}/* + else + mkdir ${VAGRANT_DIR} + fi +fi +chmod 775 ${VAGRANT_DIR} + +# Execution path +RUN_PATH="${0%/*}" +PATH_DEV_MEC=`pwd`/../../etsi_mec +SRC_MEC_PATH=~/dev/STF569_Mec + +# Update ETSI Framework files +echo 'Merging ETSI Framework files' +FWK_SRC_PATH=${SRC_MEC_PATH}/ccsrc +FWK_DST_PATH=${PATH_DEV_MEC}/framework +FWK_DIR_LIST_HH=`find ${FWK_SRC_PATH}/Protocols/ -name "*.h*" -type f` +for i in ${FWK_DIR_LIST_HH} +do + BN=`basename $i` + s1=`sha256sum -b $i | cut -d' ' -f1` + s2=`sha256sum -b ${FWK_DST_PATH}/include/${BN} | cut -d' ' -f1` + if [ "${s1}" != "${s2}" ] + then + cp ${FWK_DST_PATH}/include/${BN} ${VAGRANT_DIR} + if [ -f ${FWK_DST_PATH}/include/${BN}~ ] + then + rm ${FWK_DST_PATH}/include/${BN}~ + fi + fi +done +FWK_DIR_LIST_CC=`find ${FWK_SRC_PATH}/Protocols/ -name "*.c*" -type f` +for i in ${FWK_DIR_LIST_CC} +do + BN=`basename $i` + s1=`sha256sum -b $i | cut -d' ' -f1` + s2=`sha256sum -b ${FWK_DST_PATH}/src/${BN} | cut -d' ' -f1` + if [ "${s1}" != "${s2}" ] + then + cp ${FWK_DST_PATH}/src/${BN} ${VAGRANT_DIR} + if [ -f ${FWK_DST_PATH}/src/${BN}~ ] + then + rm ${FWK_DST_PATH}/src/${BN}~ + fi + fi +done +FWK_DIR_LIST_Y=`find ${FWK_SRC_PATH}/Protocols/ -name "*.y" -type f` +for i in ${FWK_DIR_LIST_Y} +do + BN=`basename $i` + s1=`sha256sum -b $i | cut -d' ' -f1` + s2=`sha256sum -b ${FWK_DST_PATH}/src/${BN} | cut -d' ' -f1` + if [ "${s1}" != "${s2}" ] + then + cp ${FWK_DST_PATH}/src/${BN} ${VAGRANT_DIR} + if [ -f ${FWK_DST_PATH}/src/${BN}~ ] + then + rm ${FWK_DST_PATH}/src/${BN}~ + fi + fi +done +FWK_DIR_LIST_L=`find ${FWK_SRC_PATH}/Protocols/ -name "*.l" -type f` +for i in ${FWK_DIR_LIST_L} +do + BN=`basename $i` + s1=`sha256sum -b $i | cut -d' ' -f1` + s2=`sha256sum -b ${FWK_DST_PATH}/src/${BN} | cut -d' ' -f1` + if [ "${s1}" != "${s2}" ] + then + cp ${FWK_DST_PATH}/src/${BN} ${VAGRANT_DIR} + if [ -f ${FWK_DST_PATH}/src/${BN}~ ] + then + rm ${FWK_DST_PATH}/src/${BN}~ + fi + fi +done +FWK_DIR_LIST_HH=`find ${FWK_SRC_PATH}/Framework/ -name "*.h*" -type f` +FWK_DIR_LIST_CC=`find ${FWK_SRC_PATH}/Framework/ -name "*.c*" -type f` +for i in ${FWK_DIR_LIST_HH} +do + BN=`basename $i` + s1=`sha256sum -b $i | cut -d' ' -f1` + s2=`sha256sum -b ${FWK_DST_PATH}/include/${BN} | cut -d' ' -f1` + if [ "${s1}" != "${s2}" ] + then + cp ${FWK_DST_PATH}/include/${BN} ${VAGRANT_DIR} + if [ -f ${FWK_DST_PATH}/include/${BN}~ ] + then + rm ${FWK_DST_PATH}/include/${BN}~ + fi + fi +done +for i in ${FWK_DIR_LIST_CC} +do + BN=`basename $i` + s1=`sha256sum -b $i | cut -d' ' -f1` + s2=`sha256sum -b ${FWK_DST_PATH}/src/${BN} | cut -d' ' -f1` + if [ "${s1}" != "${s2}" ] + then + cp ${FWK_DST_PATH}/src/${BN} ${VAGRANT_DIR} + if [ -f ${FWK_DST_PATH}/src/${BN}~ ] + then + rm ${FWK_DST_PATH}/src/${BN}~ + fi + fi +done +FWK_DIR_LIST_HH=`find ${FWK_SRC_PATH}/loggers/ -name "*.h*" -type f` +for i in ${FWK_DIR_LIST_HH} +do + BN=`basename $i` + s1=`sha256sum -b $i | cut -d' ' -f1` + s2=`sha256sum -b ${FWK_DST_PATH}/include/${BN} | cut -d' ' -f1` + if [ "${s1}" != "${s2}" ] + then + cp ${FWK_DST_PATH}/include/${BN} ${VAGRANT_DIR} + if [ -f ${FWK_DST_PATH}/include/${BN}~ ] + then + rm ${FWK_DST_PATH}/include/${BN}~ + fi + fi +done +FWK_DIR_LIST_CC=`find ${FWK_SRC_PATH}/loggers/ -name "*.c*" -type f` +for i in ${FWK_DIR_LIST_CC} +do + BN=`basename $i` + s1=`sha256sum -b $i | cut -d' ' -f1` + s2=`sha256sum -b ${FWK_DST_PATH}/src/${BN} | cut -d' ' -f1` + if [ "${s1}" != "${s2}" ] + then + cp ${FWK_DST_PATH}/src/${BN} ${VAGRANT_DIR} + if [ -f ${FWK_DST_PATH}/src/${BN}~ ] + then + rm ${FWK_DST_PATH}/src/${BN}~ + fi + fi +done +# Update ATS TTCN-3 files +echo 'Update TTCN-3 files' +TTCN_3_ORG_PATH=${SRC_MEC_PATH}/ttcn +TTCN_3_DST_PATH=${PATH_DEV_MEC}/src +TTCN_3_ATS_LIST='AtsNg112 LibMec LibMec/LocationAPI LibCommon LibHttp' +for i in ${TTCN_3_ATS_LIST} +do + # TTCN-3 files + LIST_TTCN_FILES=`find ${TTCN_3_ORG_PATH}/$i -name "*.ttcn" -type f` + for j in ${LIST_TTCN_FILES} + do + BN=`basename $j` + s1=`sha256sum -b $j | cut -d' ' -f1` + s2=`sha256sum -b ${TTCN_3_DST_PATH}/$i/ttcn/${BN} | cut -d' ' -f1` + if [ "${s1}" != "${s2}" ] + then + cp ${TTCN_3_DST_PATH}/$i/ttcn/${BN} ${VAGRANT_DIR} + fi + done + # JSON files + LIST_TTCN_FILES=`find ${TTCN_3_ORG_PATH}/$i -name "*.json" -type f` + for j in ${LIST_TTCN_FILES} + do + BN=`basename $j` + s1=`sha256sum -b $j | cut -d' ' -f1` + s2=`sha256sum -b ${TTCN_3_DST_PATH}/$i/ttcn/${BN} | cut -d' ' -f1` + if [ "${s1}" != "${s2}" ] + then + cp ${TTCN_3_DST_PATH}/$i/json/${BN} ${VAGRANT_DIR} + fi + done + # Other files + if [ -f ${SRC_MEC_PATH}/docs/$i/o2.cfg ] + then + s1=`sha256sum -b ${PATH_DEV_MEC}/src/$i/docs/o2.cfg | cut -d' ' -f1` + s2=`sha256sum -b ${SRC_MEC_PATH}/docs/$i/o2.cfg | cut -d' ' -f1` + if [ "${s1}" != "${s2}" ] + then + mkdir -p ${VAGRANT_DIR}/docs/$i + cp ${PATH_DEV_MEC}/src/$i/docs/o2.cfg ${VAGRANT_DIR}/docs/$i + fi + fi + if [ -f ${SRC_MEC_PATH}/etc/$i/$i.cfg ] + then + s1=`sha256sum -b ${PATH_DEV_MEC}/src/$i/etc/$i.cfg | cut -d' ' -f1` + s2=`sha256sum -b ${SRC_MEC_PATH}/etc/$i/$i.cfg | cut -d' ' -f1` + if [ "${s1}" != "${s2}" ] + then + mkdir -p ${VAGRANT_DIR}/etc/$i + cp ${PATH_DEV_MEC}/src/$i/etc/%i.cfg ${VAGRANT_DIR}/etc/$i + fi + fi +done + +TTCN_3_LIB_LIST='TestCodec' +for i in ${TTCN_3_LIB_LIST} +do + LIST_TTCN_FILES=`find ${TTCN_3_ORG_PATH}/$i -name "*.ttcn" -type f` + for j in ${LIST_TTCN_FILES} + do + BN=`basename $j` + s1=`sha256sum -b $j | cut -d' ' -f1` + s2=`sha256sum -b ${TTCN_3_DST_PATH}/$i/ttcn/${BN} | cut -d' ' -f1` + if [ "${s1}" != "${s2}" ] + then + cp ${TTCN_3_DST_PATH}/$i/ttcn/${BN} ${VAGRANT_DIR} + rm ${TTCN_3_DST_PATH}/$i/ttcn/${BN}~ + fi + done +done + + +LIST_FILES=`find ${PATH_DEV_MEC} -name "*~" -type f` +for i in ${LIST_FILES} +do + BN=$i + BN=${BN:: -1} # Remove the last character + cp ${BN} ${VAGRANT_DIR} + rm $i +done + +chmod -R 664 ${VAGRANT_DIR}/*.* +exit 0 + diff --git a/scripts/run_all.bash b/scripts/run_all.bash new file mode 100755 index 0000000..55c00e0 --- /dev/null +++ b/scripts/run_all.bash @@ -0,0 +1,47 @@ +#!/bin/bash +#set -e +#set -vx + +clear + +export LD_LIBRARY_PATH=/home/${USER}/frameworks/osip/src/osipparser2/.libs:$LD_LIBRARY_PATH + +if ! [[ $1 =~ "^[0-9]+$" ]] +then + COUNTER=$1 +else + COUNTER=1 +fi + +CURPWD=`pwd` +if [ ! "${PWD##*/}" == "objs" ] +then + cd ../objs + if [ ! $? == 0 ] + then + echo "Please move to PROJECT/obj directory" + exit 1 + fi +fi + +rm ../logs/merged.log.* + +for i in $(seq 1 1 $COUNTER) +do + LD_LIBRARY_PATH=/home/${USER}/frameworks/osip/src/osipparser2/.libs:$LD_LIBRARY_PATH ../bin/run_mtc.bash & + LD_LIBRARY_PATH=/home/${USER}/frameworks/osip/src/osipparser2/.libs:$LD_LIBRARY_PATH ../bin/run_ptcs.bash $2 + + dup=$(ps -ef | grep "$0" | grep -v grep | wc -l) + while [ ${dup} -eq 3 ] + do + sleep 1 + dup=$(ps -ef | grep "$0" | grep -v grep | wc -l) + done + sleep 1 + + mv ../logs/merged.log ../logs/merged.log.`date +'%Y%m%d%S'` +done + +exit 0 + + diff --git a/scripts/run_mtc.bash b/scripts/run_mtc.bash new file mode 100755 index 0000000..a4932c2 --- /dev/null +++ b/scripts/run_mtc.bash @@ -0,0 +1,55 @@ +#!/bin/bash +#set -evx + +clear + +CURPWD=`pwd` +if [ ! "${PWD##*/}" == "objs" ] +then + cd ../objs + if [ ! $? == 0 ] + then + echo "Please move to PROJECT/obj directory" + exit 1 + fi +fi + +TITAN_LOG_DIR=../logs +if [ ! -d ${TITAN_LOG_DIR} ] +then + mkdir ${TITAN_LOG_DIR} +else + rm -f ${TITAN_LOG_DIR}/*.log +fi + +CFG_FILES=`find ../etc -name '*.cfg'` +#LOG_FILES=`find ${TITAN_LOG_DIR} -name '*.log'` +#mv ${LOG_FILES} ../logs + +#if [ "${OSTYPE}" == "cygwin" ] +#then +# # Remove dll +# rm ./*.dll +# ## Copy the new ones +# cp ~/lib/libhelper.dll . +# cp ~/lib/libconverter.dll . +# cp ~/lib/liblogger.dll . +# cp ~/lib/libttcn3_tri.dll . +# cp ~/lib/libcomm.dll . +#fi + +echo "> cmtc: to create the MTC server" +echo "> smtc [module_name[[.control]|.testcase_name|.*]: when MyExample is connected, run the TCs in [EXECUTE] section" +echo "> emtc: Terminate MTC." +mctr ${CFG_FILES} + +LOG_FILES=`find ${TITAN_LOG_DIR} -name '*.log'` +if [ "${TITAN_LOG_DIR}" != "" ] +then + ttcn3_logmerge -o ${TITAN_LOG_DIR}/merged.log ${LOG_FILES} + ttcn3_logformat -o ${TITAN_LOG_DIR}/merged_formated.log ${TITAN_LOG_DIR}/merged.log + mv ${TITAN_LOG_DIR}/merged_formated.log ${TITAN_LOG_DIR}/merged.log + echo "log files were merged into ${TITAN_LOG_DIR}/merged.log" +fi + +cd ${CURPWD} diff --git a/scripts/run_mtc_simu.bash b/scripts/run_mtc_simu.bash new file mode 100755 index 0000000..1706187 --- /dev/null +++ b/scripts/run_mtc_simu.bash @@ -0,0 +1,55 @@ +#!/bin/bash +#set -evx + +clear + +CURPWD=`pwd` +if [ ! "${PWD##*/}" == "objs" ] +then + cd ../objs + if [ ! $? == 0 ] + then + echo "Please move to PROJECT/obj directory" + exit 1 + fi +fi + +TITAN_LOG_DIR=../logs/simu +if [ ! -d ${TITAN_LOG_DIR} ] +then + mkdir ${TITAN_LOG_DIR} +else + rm -f ${TITAN_LOG_DIR}/*.log +fi + +CFG_FILES=../etc/simu/AtsNg112.cfg +#LOG_FILES=`find ${TITAN_LOG_DIR} -name '*.log'` +#mv ${LOG_FILES} ../logs + +#if [ "${OSTYPE}" == "cygwin" ] +#then +# # Remove dll +# rm ./*.dll +# ## Copy the new ones +# cp ~/lib/libhelper.dll . +# cp ~/lib/libconverter.dll . +# cp ~/lib/liblogger.dll . +# cp ~/lib/libttcn3_tri.dll . +# cp ~/lib/libcomm.dll . +#fi + +echo "> cmtc: to create the MTC server" +echo "> smtc [module_name[[.control]|.testcase_name|.*]: when MyExample is connected, run the TCs in [EXECUTE] section" +echo "> emtc: Terminate MTC." +mctr ${CFG_FILES} + +LOG_FILES=`find ${TITAN_LOG_DIR} -name '*.log'` +if [ "${TITAN_LOG_DIR}" != "" ] +then + ttcn3_logmerge -o ${TITAN_LOG_DIR}/merged.log ${LOG_FILES} + ttcn3_logformat -o ${TITAN_LOG_DIR}/merged_formated.log ${TITAN_LOG_DIR}/merged.log + mv ${TITAN_LOG_DIR}/merged_formated.log ${TITAN_LOG_DIR}/merged.log + echo "log files were merged into ${TITAN_LOG_DIR}/merged.log" +fi + +cd ${CURPWD} diff --git a/scripts/run_ptcs.bash b/scripts/run_ptcs.bash new file mode 100755 index 0000000..869220b --- /dev/null +++ b/scripts/run_ptcs.bash @@ -0,0 +1,38 @@ +#!/bin/bash +#set -e +#set -vx + +clear + +CURPWD=`pwd` +if [ ! "${PWD##*/}" == "objs" ] +then + cd ../objs + if [ ! $? == 0 ] + then + echo "Please move to PROJECT/obj directory" + exit 1 + fi +fi + +if [ -f ./core ] +then + rm -f ./core +fi +if [ "$1" == "d" ] +then + make run_d +elif [ "$1" == "v" ] +then + make run_v +else + make run +fi +#if [ "${OSTYPE}" == "cygwin" ] +#then +# ../bin/SIPmsg.exe 127.0.0.1 12000 +#else +# ../bin/SIPmsg 127.0.0.1 12000 +#fi + +cd ${CURPWD} diff --git a/scripts/run_ptcs_simu.bash b/scripts/run_ptcs_simu.bash new file mode 100755 index 0000000..8026ab4 --- /dev/null +++ b/scripts/run_ptcs_simu.bash @@ -0,0 +1,20 @@ +#!/bin/bash +#set -e +#set -vx + +clear + +CURPWD=`pwd` +if [ ! "${PWD##*/}" == "objs" ] +then + cd ../objs + if [ ! $? == 0 ] + then + echo "Please move to PROJECT/obj directory" + exit 1 + fi +fi + +@sudo LD_LIBRARY_PATH=$(LD_LIBRARY_PATH) $(PWD)/../bin/$(EXECUTABLE) 127.0.0.1 12666 + +cd ${CURPWD} diff --git a/scripts/run_tshark.bash b/scripts/run_tshark.bash new file mode 100755 index 0000000..7b12fa4 --- /dev/null +++ b/scripts/run_tshark.bash @@ -0,0 +1,7 @@ +#!/bin/bash + +# Debug mode +#set -vx +set -e + +~/frameworks/wireshark-build/run/tshark -ieth1 -V -f"ether proto 0x8947 or udp src port 12345 or udp dst port 12345" -Tfields -eframe.time -eeth.dst -eeth.src -eeth.type -edata diff --git a/scripts/testcodec_generate_makefile.bash b/scripts/testcodec_generate_makefile.bash new file mode 100755 index 0000000..16db4f1 --- /dev/null +++ b/scripts/testcodec_generate_makefile.bash @@ -0,0 +1,242 @@ +#!/bin/bash +#set -e +set -vx + +function f_exit { + cd ${CURPWD} + + unset TTCN_FILES + unset CC_FILES + unset CFG_FILES + unset EXECUTABLE + echo $1 + exit $2 +} + +function f_usage { + echo "build.bash: This script import from External Disk the " + echo "Optional arguments:" + echo " prof: Generate a makefile including profiling options (e.g. ./build.bash prof)" + exit 0 +} + +clear + +if [ "$1" == "help" ] +then + f_usage +fi + +ATS_NAME=TestCodec + +#CURPWD=`pwd` +if [ ! "${PWD##*/}" == "objs" ] +then + cd ../objs + if [ ! $? == 0 ] + then + echo "Please move to PROJECT/obj directory" + exit 1 + fi +fi +# Remove everything +rm -fr ../objs/*.hh +rm -fr ../objs/*.cc +rm -fr ../objs/*.log +rm -fr ../objs/*.o +rm -fr ../objs/Makefile + +# Remove useless files +find .. -type f -name "*~" -exec rm {} \; +find .. -type f -name "*.bak" -exec rm {} \; +find .. -type f -name "*.log" -exec rm {} \; + +# Build XSD files if any and put them in objs directory +#XSD_PATH=${PATH_DEV_EMCOM}/xsd +XSD_PATH=${PATH_DEV_EMCOM}/null # Do not use xsd2ttcn for now, need to fix issues first +if [ -d ${XSD_PATH} ] +then + XSD_FILES=`find ${XSD_PATH} -name '*.xsd'` + + if [ "${OSTYPE}" == "cygwin" ] + then + xsd2ttcn.exe ${XSD_FILES} + else + xsd2ttcn ${XSD_FILES} + fi + if [ "$?" != "0" ] + then + f_exit "Failed to generate XSD source code" 2 + fi + XSD_FILES=`find . -name '*.ttcn'` +fi + +REFERENCES="LibCommon LibHttp LibEmcom/LibNg112 LibSip LibIms" +for i in ${REFERENCES} +do + # TTCN code + for j in `find ${PATH_DEV_EMCOM}/src/$i/ttcn -type f -name "*.ttcn"`; + do + ln -sf $j ../ttcn/`basename $j` + done + # Include source code + files=`find ${PATH_DEV_EMCOM}/src/$i/include -type f` + if [ "${files}" != " " ] + then + for j in ${files}; + do + ln -sf $j ../include/`basename $j` + done + fi + # CC source code + files=`find ${PATH_DEV_EMCOM}/src/$i/src -type f` + if [ "${files}" != " " ] + then + for j in ${files}; + do + ln -sf $j ../src/`basename $j` + done + fi +done + +# Generate the list of the TTCN-3 files +TTCN_FILES=`find .. -name '*.ttcn*'` + +# Start ATS generation - Step 1 +if [ "${OSTYPE}" == "cygwin" ] +then + rm ../bin/*.exe ../lib/*.dll + compiler.exe -e -f -g -l -L -M -n -O -t -R -U none ${TTCN_FILES} 2>&1 3>&1 | tee build.log + if [ "$?" == "1" ] + then + f_exit "Failed to compile ATS" 4 + fi +else + compiler -e -f -g -l -L -M -n -O -t -R -U none ${TTCN_FILES} 2>&1 3>&1 | tee build.log + if [ "$?" == "1" ] + then + f_exit "Failed to generate ATS source code" 6 + fi +fi + +# Sart ATS generation - Step 2 +# Create working variables +CC_FILES=`find ../src -name '*.c*'` +FWK_FILES=`find ${PATH_DEV_EMCOM}/framework/ -name '*.c*'` +CFG_FILES=`find ../etc -name '*.cfg'` + +# Sart ATS generation - Step 3 +if [ "${OSTYPE}" == "cygwin" ] +then + ttcn3_makefilegen.exe -d -f -g -m -M -R -U none -e Ats${ATS_NAME} ${TTCN_FILES} ${CC_FILES} ${FWK_FILES} ${CFG_FILES} | tee --append build.log + if [ "$?" == "1" ] + then + f_exit "Failed to compile ATS" 5 + fi +else + ttcn3_makefilegen -d -f -g -m -M -R -U none -e Ats${ATS_NAME} ${TTCN_FILES} ${CC_FILES} ${FWK_FILES} ${CFG_FILES} | tee --append build.log + if [ "$?" == "1" ] + then + f_exit "Failed to generate ATS source code" 7 + fi +fi + +# Bug xsd2ttcn +for i in ${XSD_FILES} +do + VARIANT='s/ variant (\[\-\]) ;//g' + sed --in-place "${VARIANT}" $i +done + +# Remove port skeletons to use src/ +for i in `ls ../include/*.hh` +do + if [ -f ./`basename $i` ] + then + rm ./`basename $i` + fi +done +for i in `ls ../src/*.cc` +do + if [ -f ./`basename $i` ] + then + rm ./`basename $i` + fi +done + +# Check if Makefile was generated +if [ ! -f ./Makefile ] +then + f_exit "Failed to generate ATS source code" 8 +fi + +# Patch ATS generated files +#./bin/patch.bash 2>&1 3>&1 | tee --append build.log +# Add compiler/linker options +# -DASN_DISABLE_OER_SUPPORT is required for CAMCodec and DENMCodec +if [ "$1" == "prof" ] +then + if [ "${OSTYPE}" == "cygwin" ] + then + CXXFLAGS_DEBUG_MODE='s/-Wall/-pg -Wall -std=c++11 -fPIC -D_XOPEN_SOURCE=700 -DAS_USE_SSL -DENABLE_TRACE -pthreads -fstack-check -fstack-protector -fsanitize=leak -fsanitize=address -fno-omit-frame-pointer/g' + else + CXXFLAGS_DEBUG_MODE='s/-Wall/-pg -Wall -std=c++11 -fPIC -DAS_USE_SSL -DENABLE_TRACE -pthreads -fstack-check -fstack-protector -fsanitize=leak -fsanitize=address -fno-omit-frame-pointer/g' + fi + LDFLAGS_DEBUG_MODE='s/LDFLAGS = /LDFLAGS = -pg -pthread -fPIC -fstack-check -fstack-protector -fsanitize=leak -fsanitize=address -fno-omit-frame-pointer/g' +else + if [ "${OSTYPE}" == "cygwin" ] + then + CXXFLAGS_DEBUG_MODE='s/-Wall/-ggdb -O0 -Wall -std=c++11 -fPIC -DAS_USE_SSL -DENABLE_TRACE -D_XOPEN_SOURCE=700 -pthread -fstack-check -fstack-protector -fsanitize=leak -fsanitize=address -fno-omit-frame-pointer/g' + else + CXXFLAGS_DEBUG_MODE='s/-Wall/-ggdb -O0 -Wall -std=c++11 -fPIC -DAS_USE_SSL -DENABLE_TRACE -pthread -fstack-check -fstack-protector -fsanitize=leak -fsanitize=address -fno-omit-frame-pointer/g' + fi + LDFLAGS_DEBUG_MODE='s/LDFLAGS = /LDFLAGS = -g -pthread -fPIC -fstack-check -fstack-protector -fsanitize=address -fno-omit-frame-pointer -fsanitize=leak -fsanitize=address -fno-omit-frame-pointer/g' +fi +ADD_INCLUDE='/CPPFLAGS = /a\\CPPFLAGS += -I/usr/local/share -I$(PATH_DEV_EMCOM)/include -I$(PATH_DEV_EMCOM)/framework/include -I../include -I../../LibEmcom/Common/include -I../../LibEmcom/LibNg112/include -I$(HOME_FRAMEWORKS)/osip/include -I$(HOME_INC) -I.' +ADD_LIBRARIES='s/LINUX_LIBS = -lxml2/LINUX_LIBS = -lrt -lxml2 -lpcap -lstdc++fs -lssl -L\$\(HOME_FRAMEWORKS\)\/osip\/src\/osipparser2\/\.libs -losipparser2/g' +sed --in-place "${CXXFLAGS_DEBUG_MODE}" ./Makefile +sed --in-place "${LDFLAGS_DEBUG_MODE}" ./Makefile +sed --in-place "${ADD_INCLUDE}" ./Makefile +sed --in-place "${ADD_LIBRARIES}" ./Makefile +# Update COMPILER_FLAGS +COMPILER_FLAGS='s/COMPILER_FLAGS = /COMPILER_FLAGS = -e -O /g' +sed --in-place "${COMPILER_FLAGS}" ./Makefile +# Update clean clause +CLEAN_LINE='s/$(RM) $(EXECUTABLE)/$(RM) ..\/bin\/$(EXECUTABLE) ..\/src\/*.o/g' +sed --in-place "${CLEAN_LINE}" ./Makefile +# Move binary file command +EXECUTABLE=MyExample +MV_CMD='s/all: $(TARGET) ;/all: $(TARGET) ; @if [ -f ..\/objs\/$(EXECUTABLE) ]; then mv ..\/objs\/$(EXECUTABLE) ..\/bin; fi ;/g' +sed --in-place "${MV_CMD}" ./Makefile +# Add run command +ADD_HOST='/PLATFORM = /aHOST=127.0.0.1' +ADD_PORT='/PLATFORM = /aPORT=12000' +sed --in-place "${ADD_PORT}" ./Makefile +sed --in-place "${ADD_HOST}" ./Makefile +ADD_RUN_LINE_1='$arun: all' +ADD_RUN_LINE_2='$a\\t@sudo LD_LIBRARY_PATH=$(LD_LIBRARY_PATH) $(PWD)/../bin/$(EXECUTABLE) $(HOST) $(PORT)' +sed --in-place "${ADD_RUN_LINE_1}" ./Makefile +sed --in-place "${ADD_RUN_LINE_2}" ./Makefile +ADD_RUN_LINE_1='$arun_d: all' +ADD_RUN_LINE_2='$a\\t@gdb --args $(PWD)/../bin/$(EXECUTABLE) $(HOST) $(PORT)' +sed --in-place "${ADD_RUN_LINE_1}" ./Makefile +sed --in-place "${ADD_RUN_LINE_2}" ./Makefile +ADD_RUN_LINE_1='$arun_v: all' +ADD_RUN_LINE_2='$a\\t@sudo LD_LIBRARY_PATH=$(LD_LIBRARY_PATH) valgrind -v --tool=memcheck --leak-check=yes --show-reachable=yes --track-fds=yes --run-cxx-freeres=yes $(PWD)/../bin/$(EXECUTABLE) $(HOST) $(PORT)' +sed --in-place "${ADD_RUN_LINE_1}" ./Makefile +sed --in-place "${ADD_RUN_LINE_2}" ./Makefile +# Add gendoc entry +ADD_RUN_LINE_1='$agendoc: ../docs/o2.cfg' +ADD_RUN_LINE_2='$a\\tdoxygen ../docs/o2.cfg' +sed --in-place "${ADD_RUN_LINE_1}" ./Makefile +sed --in-place "${ADD_RUN_LINE_2}" ./Makefile + +# Build all +make all 2>&1 3>&1 | tee --append build.log +if [ "$?" == "1" ] +then + f_exit "Failed to generate ATS source code" 9 +fi +export LD_LIBRARY_PATH=~/frameworks/osip/src/osipparser2/.libs:$LD_LIBRARY_PATH +../bin/Ats${ATS_NAME} -v +f_exit "Build done successfully" 0 diff --git a/scripts/titan_repos.txt b/scripts/titan_repos.txt new file mode 100644 index 0000000..fc8a2ef --- /dev/null +++ b/scripts/titan_repos.txt @@ -0,0 +1,42 @@ +https://github.com/eclipse/titan.core.git +https://github.com/eclipse/titan.TestPorts.Common_Components.Abstract_Socket.git +https://github.com/eclipse/titan.TestPorts.HTTPmsg.git +https://github.com/eclipse/titan.TestPorts.PCAPasp.git +https://github.com/eclipse/titan.TestPorts.PIPEasp.git +https://github.com/eclipse/titan.TestPorts.SCTPasp.git +https://github.com/eclipse/titan.TestPorts.SIPmsg.git +https://github.com/eclipse/titan.TestPorts.TCPasp.git +https://github.com/eclipse/titan.TestPorts.TELNETasp.git +https://github.com/eclipse/titan.TestPorts.UDPasp.git +https://github.com/eclipse/titan.ProtocolModules.COMMON.git +https://github.com/eclipse/titan.ProtocolModules.DHCP.git +https://github.com/eclipse/titan.ProtocolModules.DHCPv6.git +https://github.com/eclipse/titan.ProtocolModules.DIAMETER_ProtocolModule_Generator.git +https://github.com/eclipse/titan.ProtocolModules.DNS.git +https://github.com/eclipse/titan.ProtocolModules.ICMP.git +https://github.com/eclipse/titan.ProtocolModules.ICMPv6.git +https://github.com/eclipse/titan.ProtocolModules.IP.git +https://github.com/eclipse/titan.ProtocolModules.TCP.git +https://github.com/eclipse/titan.ProtocolModules.UDP.git +https://github.com/eclipse/titan.EclipsePlug-ins.git +https://github.com/eclipse/titan.misc.git +https://github.com/eclipse/titan.TestPorts.Common_Components.Socket-API.git +https://github.com/eclipse/titan.TestPorts.SSHCLIENTasp.git +https://github.com/eclipse/titan.TestPorts.STDINOUTmsg.git +https://github.com/eclipse/titan.TestPorts.UNIX_DOMAIN_SOCKETasp.git +https://github.com/eclipse/titan.TestPorts.IPL4asp.git +https://github.com/eclipse/titan.ProtocolModules.IPsec.git +https://github.com/eclipse/titan.ProtocolModules.JSON_v07_2006.git +https://github.com/eclipse/titan.ProtocolModules.RADIUS_ProtocolModule_Generator.git +https://github.com/eclipse/titan.ProtocolModules.WebSocket.git +https://github.com/eclipse/titan.ProtocolModules.HTTP2.0.git +https://github.com/eclipse/titan.Libraries.TCCUsefulFunctions.git +https://git.eclipse.org/r/titan/titan.ApplicationLibraries.CoAP +https://git.eclipse.org/r/titan/titan.ApplicationLibraries.MQTT +https://git.eclipse.org/r/titan/titan.ProtocolModules.CoAP.git +https://git.eclipse.org/r/titan/titan.ProtocolModules.MQTT.git +https://git.eclipse.org/r/titan/titan.ProtocolModules.SCTP.git +https://git.eclipse.org/r/titan/titan.ProtocolModules.SDP.git +https://git.eclipse.org/r/titan/titan.ProtocolModules.TLS.git +https://git.eclipse.org/r/titan/titan.TestPorts.GPIO.git +https://git.eclipse.org/r/titan/titan.TestPorts.Serial.git diff --git a/scripts/update_mec_project.bash b/scripts/update_mec_project.bash new file mode 100755 index 0000000..093f323 --- /dev/null +++ b/scripts/update_mec_project.bash @@ -0,0 +1,187 @@ +#!/bin/bash + +# Debug mode +#set -e +set -vx + +# Usage: sudo ./update_mec_project.bash +# TODO Use git clone in temporary directory + +OLDPWD=`pwd` + +# Execution path +RUN_PATH="${0%/*}" + +if [ "${VALIDATION_DIR}" == "" ] +then + VALIDATION_DIR=${VALIDATION_DIR} +fi + +USER=`whoami` +CHOWN_USER_GROUP=${USER}:${USER} +SRC_MEC_PATH=${VALIDATION_DIR}/dev/STF569_Mec + +if [ "${PATH_DEV_MEC}" == "" ] +then + PATH_DEV_MEC=${VALIDATION_DIR}/dev/etsi_mec +fi +echo ${PATH_DEV_MEC} + +if [ -d ${PATH_DEV_MEC} ] +then + if [ -f ${HOME}/tmp/mec.tar.bz2 ] + then + mv ${HOME}/tmp/mec.tar.bz2 ${VALIDATION_DIR}/tmp/mec.tar.`date +'%Y%m%d'`.bz2 + fi + find ${PATH_DEV_MEC} -name "*.o" -exec rm {} \; + tar jchvf ${HOME}/tmp/mec.tar.bz2 ${PATH_DEV_MEC} + rm -fr ${PATH_DEV_MEC} +fi + +# Check if target directory exist +if [ ! -d ${PATH_DEV_MEC} ] +then + mkdir -p ${PATH_DEV_MEC}/json ${PATH_DEV_MEC}/framework ${PATH_DEV_MEC}/include ${PATH_DEV_MEC}/bin ${PATH_DEV_MEC}/lib ${PATH_DEV_MEC}/objs ${PATH_DEV_MEC}/src ${PATH_DEV_MEC}/docs +fi + +# Update JSON files +echo 'Updating JSON files' +JSON_SRC_PATH=${SRC_MEC_PATH}/json +JSON_DST_PATH=${PATH_DEV_MEC}/json +cp ${JSON_SRC_PATH}/*.json ${JSON_DST_PATH} +cp ${JSON_SRC_PATH}/*.dtd ${JSON_DST_PATH} + +# Update ETSI Framework files +echo 'Updating ETSI Framework files' +FWK_SRC_PATH=${SRC_MEC_PATH}/ccsrc +FWK_DST_PATH=${PATH_DEV_MEC}/framework +mkdir -p ${FWK_DST_PATH}/src ${FWK_DST_PATH}/include +chmod -R 775 ${FWK_DST_PATH} +# Create link to TITAN Abstract_Socket +ln -sf $TOP/../titan.TestPorts.Common_Components.Abstract_Socket/src/Abstract_Socket.cc ${FWK_DST_PATH}/src/Abstract_Socket.cc +ln -sf $TOP/../titan.TestPorts.Common_Components.Abstract_Socket/src/Abstract_Socket.hh ${FWK_DST_PATH}/include/Abstract_Socket.hh +FWK_DIR_LIST_HH=`find ${FWK_SRC_PATH}/Protocols/ -name "*.h*" -type f` +FWK_DIR_LIST_THH=`find ${FWK_SRC_PATH}/Protocols/ -name "*.t.h*" -type f` +FWK_DIR_LIST_CC=`find ${FWK_SRC_PATH}/Protocols/ -name "*.c*" -type f` +FWK_DIR_LIST_L=`find ${FWK_SRC_PATH}/Protocols/ -name "*.l" -type f` +FWK_DIR_LIST_Y=`find ${FWK_SRC_PATH}/Protocols/ -name "*.y" -type f` +for i in ${FWK_DIR_LIST_HH} +do + cp $i ${FWK_DST_PATH}/include +done +for i in ${FWK_DIR_LIST_THH} +do + cp $i ${FWK_DST_PATH}/include +done +for i in ${FWK_DIR_LIST_CC} +do + cp $i ${FWK_DST_PATH}/src +done +if [ "${FWK_DIR_LIST_L}" != "" ] +then + for i in ${FWK_DIR_LIST_L} + do + cp $i ${FWK_DST_PATH}/src + done +fi +if [ "${FWK_DIR_LIST_Y}" != "" ] +then + for i in ${FWK_DIR_LIST_Y} + do + cp $i ${FWK_DST_PATH}/src + done +fi +FWK_DIR_LIST_HH=`find ${FWK_SRC_PATH}/Framework/ -name "*.h*" -type f` +FWK_DIR_LIST_CC=`find ${FWK_SRC_PATH}/Framework/ -name "*.c*" -type f` +for i in ${FWK_DIR_LIST_HH} +do + cp $i ${FWK_DST_PATH}/include +done +for i in ${FWK_DIR_LIST_CC} +do + cp $i ${FWK_DST_PATH}/src +done + +# Update ATS TTCN-3 files +echo 'Update TTCN-3 files' +TTCN_3_ORG_PATH=${SRC_MEC_PATH}/ttcn +TTCN_3_DST_PATH=${PATH_DEV_MEC}/src +CC_SRC_PATH=${SRC_MEC_PATH}/ccsrc +TTCN_3_ATS_LIST='AtsMec TestCodec' +for i in ${TTCN_3_ATS_LIST} +do + if [ ! -d ${TTCN_3_DST_PATH}/$i ] + then + mkdir -p ${TTCN_3_DST_PATH}/$i/bin ${TTCN_3_DST_PATH}/$i/lib ${TTCN_3_DST_PATH}/$i/src ${TTCN_3_DST_PATH}/$i/include ${TTCN_3_DST_PATH}/$i/ttcn ${TTCN_3_DST_PATH}/$i/objs ${TTCN_3_DST_PATH}/$i/etc ${TTCN_3_DST_PATH}/$i/docs + chmod -R 775 ${TTCN_3_DST_PATH}/$i + fi + cp ${TTCN_3_ORG_PATH}/$i/*.ttcn ${TTCN_3_DST_PATH}/$i/ttcn + cp ${TTCN_3_ORG_PATH}/../etc/$i/*.cfg ${TTCN_3_DST_PATH}/$i/etc + if [ -d ${TTCN_3_ORG_PATH}/../etc_simu ] + then + mkdir -p ${TTCN_3_ORG_PATH}/../etc_simu + cp ${TTCN_3_ORG_PATH}/../etc_simu/$i/*.cfg ${TTCN_3_DST_PATH}/$i/etc_simu + fi + cp ${TTCN_3_ORG_PATH}/../docs/$i/o2.cfg ${TTCN_3_DST_PATH}/$i/docs +done + +# Update libraries & CC files +TTCN_3_LIB_LIST='LibMec LibMec/LocationAPI LibHttp LibCommon' +for i in ${TTCN_3_LIB_LIST} +do + if [ ! -d ${TTCN_3_DST_PATH}/$i ] + then + mkdir -p ${TTCN_3_DST_PATH}/$i/docs ${TTCN_3_DST_PATH}/$i/src ${TTCN_3_DST_PATH}/$i/include ${TTCN_3_DST_PATH}/$i/ttcn ${TTCN_3_DST_PATH}/$i/json + fi + cp ${TTCN_3_ORG_PATH}/$i/*.ttcn ${TTCN_3_DST_PATH}/$i/ttcn + # Update files + if [ "$i" == "LibMec/LocationAPI" ] + then + cp ${TTCN_3_ORG_PATH}/$i/ttcn/*.ttcn ${TTCN_3_DST_PATH}/$i/ttcn + cp ${TTCN_3_ORG_PATH}/$i/json/*.json ${TTCN_3_DST_PATH}/$i/json + cp ${CC_SRC_PATH}/include/$i/*.hh ${TTCN_3_DST_PATH}/$i/include + cp ${CC_SRC_PATH}/src/$i/*.cc ${TTCN_3_DST_PATH}/$i/src + elif [ "$i" == "LibHttp" ] + then + cp ${TTCN_3_ORG_PATH}/LibIts/ttcn/Http/*.ttcn ${TTCN_3_DST_PATH}/$i/ttcn + cp ${CC_SRC_PATH}/EncDec/$i/*_Encdec.cc ${TTCN_3_DST_PATH}/$i/src + cp ${CC_SRC_PATH}/Ports/$i/*.hh ${TTCN_3_DST_PATH}/$i/include + cp ${CC_SRC_PATH}/Ports/$i/*.cc ${TTCN_3_DST_PATH}/$i/src + cp ${CC_SRC_PATH}/include/$i/*.hh ${TTCN_3_DST_PATH}/$i/include + cp ${CC_SRC_PATH}/src/$i/*.cc ${TTCN_3_DST_PATH}/$i/src + # Patch due to svn/gitlab moving + cp ${SRC_MEC_PATH}/ttcn/patch_lib_http/*.ttcn ${TTCN_3_DST_PATH}/$i/ttcn + elif [ "$i" == "LibCommon" ] + then + cp ${TTCN_3_ORG_PATH}/$i/ttcn/*.ttcn ${TTCN_3_DST_PATH}/$i/ttcn + # Patch TITAN due to TTCN-3 compiler issues + cp ${SRC_MEC_PATH}/ttcn/patch_lib_common_titan/*.ttcn ${TTCN_3_DST_PATH}/$i/ttcn + fi +done + +# Apply patches +PATH_PATCHES=`pwd` +if [ -d ${PATH_PATCHES} ] +then + # Update Mec + cp ${PATH_PATCHES}/mec_generate_makefile.bash ${PATH_DEV_MEC}/src/AtsMec/bin + cp ${PATH_PATCHES}/run_mtc.bash ${PATH_DEV_MEC}/src/AtsMec/bin + cp ${PATH_PATCHES}/run_ptcs.bash ${PATH_DEV_MEC}/src/AtsMec/bin + cp ${PATH_PATCHES}/run_all.bash ${PATH_DEV_MEC}/src/AtsMec/bin + cp ${PATH_PATCHES}/run_*_simu.bash ${PATH_DEV_MEC}/src/AtsMec/bin + # Update TestCodec + cp ${PATH_PATCHES}/testcodec_generate_makefile.bash ${PATH_DEV_MEC}/src/TestCodec/bin + cp ${PATH_PATCHES}/run_mtc.bash ${PATH_DEV_MEC}/src/TestCodec/bin + cp ${PATH_PATCHES}/run_ptcs.bash ${PATH_DEV_MEC}/src/TestCodec/bin + cp ${PATH_PATCHES}/run_all.bash ${PATH_DEV_MEC}/src/TestCodec/bin +fi + +# Set rights +find ${PATH_DEV_MEC} -type f -exec chmod 664 {} \; +find ${PATH_DEV_MEC} -name "*.bash" -type f -exec chmod 775 {} \; +find ${PATH_DEV_MEC} -type d -exec chmod 775 {} \; +chown -R ${CHOWN_USER_GROUP} ${PATH_DEV_MEC} + +cd ${OLDPWD} + +exit 0 diff --git a/ttcn/AtsMec/AtsMec_LocationAPI_TestCases.ttcn b/ttcn/AtsMec/AtsMec_LocationAPI_TestCases.ttcn new file mode 100644 index 0000000..1cd1252 --- /dev/null +++ b/ttcn/AtsMec/AtsMec_LocationAPI_TestCases.ttcn @@ -0,0 +1,97 @@ +/** + * @author ETSI / STF569 + * @version $URL:$ + * $ID:$ + * @desc This module provides the MEC test cases. + * @copyright ETSI Copyright Notification + * No part may be reproduced except as authorized by written permission. + * The copyright and the foregoing restriction extend to reproduction in all media. + * All rights reserved. + * @see ETSI GS MEC 003, Draft ETSI GS MEC 013 V2.0.3 (2018-10) + */ +module AtsMec_LocationAPI_TestCases { + + // Libcommon + import from LibCommon_Time all; + import from LibCommon_VerdictControl all; + import from LibCommon_Sync all; + + // LibHttp + import from LibItsHttp_TypesAndValues all; + import from LibItsHttp_Functions all; + import from LibItsHttp_Templates all; + import from LibItsHttp_JsonTemplates all; + import from LibItsHttp_TestSystem all; + + // LibMec_LocationAPI + import from LocationAPI_TypesAndValues all; + import from LocationAPI_Templates all; + import from LocationAPI_Pics all; + import from LocationAPI_Pixits all; + + // LibMec + import from LibMec_Functions all; + import from LibMec_Pics all; + import from LibMec_Pixits all; + + group me_app_role { + + /** + * @desc Check that the IUT responds with a list for the location of User Equipments when queried by a MEC Application + * @see https://forge.etsi.org/gitlab/mec/MEC-tests/blob/master/Test%20Purposes/Plat/Mp1/UeLocation/PlatUeLocation.tplan2 + */ + testcase TC_MEC_PLAT_MP1_LOC_BV_001() runs on HttpComponent system HttpTestAdapter { + // Local variables + var HeaderLines v_headers; + var HttpMessage v_response; + + // Test control + if (not(PICS_ME_APP_IUT) or not(PICS_LOCATION_API_SUPPORTED)) { + log("*** " & testcasename() & ": PICS_ME_APP_IUT and PICS_LOCATION_API_SUPPORTED required for executing the TC ***"); + setverdict(inconc); + stop; + } + + // Test component configuration + f_cf_01_http_up(); + + // Test adapter configuration + + // Preamble + f_init_default_headers_list(-, -, v_headers); + httpPort.send( + m_http_request( + m_http_request_get( + PICS_ME_APP_Q_ZONE_ID_URI & oct2char(unichar2oct(PX_ZONE_ID, "UTF-8")), + v_headers + ) + ) + ); + f_selfOrClientSyncAndVerdictTestBody(c_prDone, e_success); + + // Test Body + tc_ac.start; + alt { + [] httpPort.receive( + mw_http_response( + mw_http_response_ok( + mw_http_message_body_json( + mw_body_json_user_info( + mw_user_info(-, -, PX_ZONE_ID) + ))))) -> value v_response { + log("*** " & testcasename() & ": PASS: IUT successfully responds with a ZoneId ***"); + f_selfOrClientSyncAndVerdictTestBody(c_tbDone, e_success); + } + [] tc_ac.timeout { + log("*** " & testcasename() & ": INCONC: Expected message not received ***"); + f_selfOrClientSyncAndVerdictTestBody(c_tbDone, e_timeout); + } + } // End of 'alt' statement + + // Postamble + f_cf_01_http_down(); + } // End of testcase TC_MEC_PLAT_MP1_LOC_BV_001 + + } // End of group me_app_role + +} // End of module AtsMec_TestCases diff --git a/ttcn/AtsMec/AtsMec_TestControl.ttcn b/ttcn/AtsMec/AtsMec_TestControl.ttcn new file mode 100644 index 0000000..83ebcee --- /dev/null +++ b/ttcn/AtsMec/AtsMec_TestControl.ttcn @@ -0,0 +1,21 @@ +module AtsMec_TestControl { + + // LibMec_LocationAPI + import from LocationAPI_Pics all; + + // LibMec + import from LibMec_Pics all; + + // AtsMec + import from AtsMec_LocationAPI_TestCases all; + + control { + + if (PICS_ME_APP_IUT) { + if (PICS_LOCATION_API_SUPPORTED) { + execute(TC_MEC_PLAT_MP1_LOC_BV_001()); + } + } + } + +} // End of module AtsMec_TestControl diff --git a/ttcn/LibMec/LocationAPI/json/LocationAPI.json b/ttcn/LibMec/LocationAPI/json/LocationAPI.json new file mode 100644 index 0000000..4e87da0 --- /dev/null +++ b/ttcn/LibMec/LocationAPI/json/LocationAPI.json @@ -0,0 +1,1641 @@ +{ + "swagger": "2.0", + "info": { + "title": "Location API", + "version": "1.1.1", + "description": "The ETSI MEC ISG MEC012 Location API described using OpenAPI. The API is based on the Open Mobile Alliance's specification RESTful Network API for Zonal Presence", + "license": { + "name": "ETSI Forge copyright notice", + "url": "https://forge.etsi.org/etsi-forge-copyright-notice.txt" + } + }, + "externalDocs": { + "description": "ETSI MEC013 V1.1.1 Location Service API", + "url": "http://www.etsi.org/deliver/etsi_gs/MEC/001_099/013/01.01.01_60/gs_mec013v010101p.pdf" + }, + "host": "127.0.0.1:8081", + "basePath": "/exampleAPI/location/v1", + "schemes": [ + "http", + "https" + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + { + "name": "zones" + }, + { + "name": "users" + }, + { + "name": "subscriptions" + } + ], + "parameters": { + "Body.UserTrackingSubscription": { + "name": "userTrackingSubscription", + "in": "body", + "description": "User Tracking Subscription", + "required": true, + "schema": { + "$ref": "#/definitions/UserTrackingSubscription" + } + }, + "Body.ZonalTrafficSubscription": { + "name": "zonalTrafficSubscription", + "in": "body", + "description": "Zonal Traffic Subscription", + "required": true, + "schema": { + "$ref": "#/definitions/ZonalTrafficSubscription" + } + }, + "Body.ZoneStatusSubscription": { + "name": "zoneStatusSubscription", + "in": "body", + "description": "Zone Status Subscription", + "required": true, + "schema": { + "$ref": "#/definitions/ZoneStatusSubscription" + } + }, + "Path.AccessPointId": { + "name": "accessPointId", + "in": "path", + "description": "Access Point ID", + "required": true, + "type": "string" + }, + "Path.SubscriptionId": { + "name": "subscriptionId", + "in": "path", + "description": "Subscription ID", + "required": true, + "type": "string" + }, + "Path.UserId": { + "name": "userId", + "in": "path", + "description": "User ID", + "required": true, + "type": "string" + }, + "Path.ZoneId": { + "name": "zoneId", + "in": "path", + "description": "Zone ID", + "required": true, + "type": "string" + }, + "Query.AccessPointId": { + "name": "accessPointId", + "in": "query", + "description": "Identifier of access point, reference \"definitions\" for string format", + "required": false, + "type": "string" + }, + "Query.InterestRealm": { + "name": "interestRealm", + "in": "query", + "description": "Interest realm of access point (e.g. geographical area, a type of industry etc.).", + "required": false, + "type": "string" + }, + "Query.ZoneId": { + "name": "zoneId", + "in": "query", + "description": "Zone ID", + "required": true, + "type": "string" + } + }, + "paths": { + "/zones": { + "get": { + "tags": [ + "zones" + ], + "operationId": "zonesGet", + "description": "Used to get a list of identifiers for zones authorized for use by the application.", + "produces": [ + "application/json" + ], + "responses": { + "200": { + "description": "Successful response to a query regarding the status of a zone", + "schema": { + "properties": { + "zoneList": { + "$ref": "#/definitions/ZoneList" + } + } + }, + "examples": { + "application/json": { + "zoneList": { + "zone": [ + { + "zoneId": "zone01", + "numberOfAccessPoints": "3", + "numberOfUnserviceableAccessPoints": "1", + "numberOfUsers": "10", + "resourceURL": "http://example.com/exampleAPI/location/v1/zones/zone01" + }, + { + "zoneId": "zone02", + "numberOfAccessPoints": "12", + "numberOfUnserviceableAccessPoints": "0", + "numberOfUsers": "36", + "resourceURL": "http://example.com/exampleAPI/location/v1/zones/zone02" + } + ], + "resourceURL": "http://example.com/exampleAPI/location/v1/zones" + } + } + } + } + } + } + }, + "/zones/{zoneId}": { + "parameters": [ + { + "$ref": "#/parameters/Path.ZoneId" + } + ], + "get": { + "tags": [ + "zones" + ], + "operationId": "zonesGetById", + "description": "Used to get the status of a zone.", + "produces": [ + "application/json" + ], + "responses": { + "200": { + "description": "Successful response to a query regarding the status of a zone", + "schema": { + "properties": { + "zoneInfo": { + "$ref": "#/definitions/ZoneInfo" + } + } + }, + "examples": { + "application/json": { + "zoneInfo": { + "zoneId": "zone01", + "numberOfAccessPoints": "3", + "numberOfUnserviceableAccessPoints": "1", + "numberOfUsers": "10", + "resourceURL": "http://example.com/exampleAPI/location/v1/zones/zone01" + } + } + } + } + } + } + }, + "/zones/{zoneId}/accessPoints": { + "parameters": [ + { + "$ref": "#/parameters/Path.ZoneId" + } + ], + "get": { + "tags": [ + "zones" + ], + "operationId": "zonesByIdGetAps", + "description": "Access point status can be retrieved for sets of access points matching attribute in the request.", + "produces": [ + "application/json" + ], + "parameters": [ + { + "$ref": "#/parameters/Query.InterestRealm" + } + ], + "responses": { + "200": { + "description": "Successful response to a query a named set of access point status request", + "schema": { + "properties": { + "accessPointList": { + "$ref": "#/definitions/AccessPointList" + } + } + }, + "examples": { + "application/json": { + "accessPointList": { + "zoneId": "zone01", + "accessPoint": [ + { + "accessPointId": "001010000000000000000000000000001", + "locationInfo": { + "latitude": "90.123", + "longitude": "80.123", + "altitude": "10.0", + "accuracy": "0" + }, + "connectionType": "Macro", + "operationStatus": "Serviceable", + "numberOfUsers": "5", + "interestRealm": "LA", + "resourceURL": "http://example.com/exampleAPI/location/v1/zones/zone01/accessPoints/ap001" + }, + { + "accessPointId": "001010000000000000000000000000010", + "locationInfo": { + "latitude": "91.123", + "longitude": "81.123", + "altitude": "12.0", + "accuracy": "1" + }, + "connectionType": "Macro", + "operationStatus": "Unserviceable", + "numberOfUsers": "0", + "interestRealm": "DC", + "resourceURL": "http://example.com/exampleAPI/location/v1/zones/zone01/accessPoints/ap002" + }, + { + "accessPointId": "001010000000000000000000000000011", + "locationInfo": { + "latitude": "93.123", + "longitude": "83.123", + "altitude": "16.0", + "accuracy": "3" + }, + "connectionType": "Macro", + "operationStatus": "Serviceable", + "numberOfUsers": "5", + "interestRealm": "NJ", + "resourceURL": "http://example.com/exampleAPI/location/v1/zones/zone01/accessPoints/ap003" + } + ], + "resourceURL": "http://example.com/exampleAPI/location/v1/zones/zone01/accessPoints" + } + } + } + } + } + } + }, + "/zones/{zoneId}/accessPoints/{accessPointId}": { + "parameters": [ + { + "$ref": "#/parameters/Path.ZoneId" + }, + { + "$ref": "#/parameters/Path.AccessPointId" + } + ], + "get": { + "tags": [ + "zones" + ], + "operationId": "zonesByIdGetApsById", + "description": "Access point status can be retrieved for sets of access points matching attribute in the request.", + "produces": [ + "application/json" + ], + "responses": { + "200": { + "description": "Successful response to a query a named set of access point status request", + "schema": { + "properties": { + "accessPointInfo": { + "$ref": "#/definitions/AccessPointInfo" + } + } + }, + "examples": { + "application/json": { + "accessPointInfo": { + "accessPointId": "001010000000000000000000000000001", + "locationInfo": { + "latitude": "90.123", + "longitude": "80.123", + "altitude": "10.0", + "accuracy": "0" + }, + "connectionType": "Macro", + "operationStatus": "Serviceable", + "numberOfUsers": "5", + "interestRealm": "LA", + "resourceURL": "http://example.com/exampleAPI/location/v1/zones/zone001/accessPoints/ap001" + } + } + } + } + } + } + }, + "/users": { + "get": { + "tags": [ + "users" + ], + "operationId": "usersGet", + "description": "Users currently using a zone may be retrieved for sets of access points matching attribute in the request", + "produces": [ + "application/json" + ], + "parameters": [ + { + "$ref": "#/parameters/Query.ZoneId" + }, + { + "$ref": "#/parameters/Query.AccessPointId" + } + ], + "responses": { + "200": { + "description": "Successful response to a query users within a zone request", + "schema": { + "properties": { + "userList": { + "$ref": "#/definitions/UserList" + } + } + }, + "examples": { + "application/json": { + "userList": { + "user": [ + { + "address": "acr:192.0.2.1", + "accessPointId": "001010000000000000000000000000001", + "zoneId": "zone01", + "resourceURL": "http://example.com/exampleAPI/location/v1/users/acr%3A192.0.2.1" + }, + { + "address": "acr:192.0.2.2", + "accessPointId": "001010000000000000000000000000001", + "zoneId": "zone01", + "resourceURL": "http://example.com/exampleAPI/location/v1/users/acr%3A192.0.2.2" + }, + { + "address": "acr:192.0.2.3", + "accessPointId": "001010000000000000000000000000010", + "zoneId": "zone01", + "resourceURL": "http://example.com/exampleAPI/location/v1/users/acr%3A192.0.2.3" + }, + { + "address": "acr:192.0.2.4", + "accessPointId": "001010000000000000000000000000001", + "zoneId": "zone02", + "resourceURL": "http://example.com/exampleAPI/location/v1/users/acr%3A192.0.2.4" + }, + { + "address": "acr:192.0.2.5", + "accessPointId": "001010000000000000000000000000010", + "zoneId": "zone02", + "resourceURL": "http://example.com/exampleAPI/location/v1/users/acr%3A192.0.2.5" + } + ], + "resourceURL": "http://example.com/exampleAPI/location/v1/users" + } + } + } + } + } + } + }, + "/users/{userId}": { + "parameters": [ + { + "$ref": "#/parameters/Path.UserId" + } + ], + "get": { + "tags": [ + "users" + ], + "operationId": "usersGetById", + "description": "Users currently using a zone may be retrieved for sets of access points matching attribute in the request", + "produces": [ + "application/json" + ], + "responses": { + "200": { + "description": "Successful response to a query users within a zone request", + "schema": { + "properties": { + "userInfo": { + "$ref": "#/definitions/UserInfo" + } + } + }, + "examples": { + "application/json": { + "userInfo": { + "address": "acr:192.0.2.1", + "accessPointId": "001010000000000000000000000000001", + "zoneId": "zone01", + "resourceURL": "http://example.com/exampleAPI/location/v1/users/acr%3A192.0.2.1", + "locationInfo": { + "latitude": "90.123", + "longitude": "80.123", + "altitude": "10.0", + "accuracy": "0" + }, + "contextLocationInfo": "GroundFloor" + } + } + } + } + } + } + }, + "/subscriptions/zonalTraffic": { + "get": { + "tags": [ + "subscriptions" + ], + "operationId": "zonalTrafficSubGet", + "description": "This operation is used for retrieving all active subscriptions to zonal traffic change notifications.", + "produces": [ + "application/json" + ], + "responses": { + "200": { + "description": "Response to retrieve zonal traffic subscriptions", + "schema": { + "type": "object", + "properties": { + "notificationSubscriptionList": { + "type": "object", + "properties": { + "zonalTrafficSubscription": { + "type": "array", + "items": { + "$ref": "#/definitions/ZonalTrafficSubscription" + } + }, + "resourceURL": { + "$ref": "#/definitions/ResourceURL" + } + } + } + } + }, + "examples": { + "application/json": { + "notificationSubscriptionList": { + "zonalTrafficSubscription": [ + { + "clientCorrelator": "0123", + "resourceURL": "http://example.com/exampleAPI/location/v1/subscriptions/zonalTraffic/subscription123", + "callbackReference": { + "notifyURL": "http://clientApp.example.com/location_notifications/123456" + }, + "zoneId": "zone01", + "interestRealm": "LA", + "userEventCriteria": "Transferring" + }, + { + "clientCorrelator": "0124", + "resourceURL": "http://example.com/exampleAPI/location/v1/subscriptions/zonalTraffic/subscription124", + "callbackReference": { + "notifyURL": "http://clientApp.example.com/location_notifications/123457" + }, + "zoneId": "zone02", + "interestRealm": "LA", + "userEventCriteria": "Transferring" + } + ], + "resourceURL": "http://example.com/exampleAPI/location/v1/zonalTraffic" + } + } + } + } + } + }, + "post": { + "tags": [ + "subscriptions" + ], + "operationId": "zonalTrafficSubPost", + "description": "This operation is used for creating a new subscription to zonal traffic change notification.", + "produces": [ + "application/json" + ], + "parameters": [ + { + "$ref": "#/parameters/Body.ZonalTrafficSubscription" + } + ], + "responses": { + "201": { + "description": "Response to create new zonal traffic subscription", + "schema": { + "properties": { + "zonalTrafficSubscription": { + "$ref": "#/definitions/ZonalTrafficSubscription" + } + } + }, + "examples": { + "application/json": { + "zonalTrafficSubscription": { + "clientCorrelator": "0123", + "resourceURL": "http://example.com/exampleAPI/location/v1/subscriptions/zonalTraffic/subscription123", + "callbackReference": { + "notifyURL": "http://clientApp.example.com/location_notifications/123456" + }, + "zoneId": "zone01", + "interestRealm": "LA", + "userEventCriteria": "Transferring" + } + } + } + } + } + } + }, + "/subscriptions/zonalTraffic/{subscriptionId}": { + "parameters": [ + { + "$ref": "#/parameters/Path.SubscriptionId" + } + ], + "get": { + "tags": [ + "subscriptions" + ], + "operationId": "zonalTrafficSubGetById", + "description": "This operation is used for updating an individual subscription to zonal traffic change notification.", + "produces": [ + "application/json" + ], + "responses": { + "200": { + "description": "Response to retrieve individual zonal traffic subscription", + "schema": { + "properties": { + "zonalTrafficSubscription": { + "$ref": "#/definitions/ZonalTrafficSubscription" + } + } + }, + "examples": { + "application/json": { + "zonalTrafficSubscription": { + "clientCorrelator": "0123", + "resourceURL": "http://example.com/exampleAPI/location/v1/subscriptions/zonalTraffic/subscription123", + "callbackReference": { + "notifyURL": "http://clientApp.example.com/location_notifications/123456" + }, + "zoneId": "zone01", + "interestRealm": "LA", + "userEventCriteria": "Transferring" + } + } + } + } + } + }, + "put": { + "tags": [ + "subscriptions" + ], + "operationId": "zonalTrafficSubPutById", + "description": "This operation is used for updating an individual subscription to zonal traffic change notification.", + "produces": [ + "application/json" + ], + "parameters": [ + { + "$ref": "#/parameters/Body.ZonalTrafficSubscription" + } + ], + "responses": { + "200": { + "description": "Response to update individual zonal traffic subscription", + "schema": { + "properties": { + "zonalTrafficSubscription": { + "$ref": "#/definitions/ZonalTrafficSubscription" + } + } + }, + "examples": { + "application/json": { + "zonalTrafficSubscription": { + "clientCorrelator": "0123", + "resourceURL": "http://example.com/exampleAPI/location/v1/subscriptions/zonalTraffic/subscription123", + "callbackReference": { + "notifyURL": "http://clientApp.example.com/location_notifications/123456" + }, + "zoneId": "zone01", + "interestRealm": "LA", + "userEventCriteria": "Transferring" + } + } + } + } + } + }, + "delete": { + "tags": [ + "subscriptions" + ], + "operationId": "zonalTrafficSubDelById", + "description": "This operation is used for cancelling a subscription and stopping corresponding notifications.", + "produces": [ + "application/json" + ], + "responses": { + "204": { + "description": "No content" + } + } + } + }, + "/subscriptions/userTracking": { + "get": { + "tags": [ + "subscriptions" + ], + "operationId": "userTrackingSubGet", + "description": "This operation is used for retrieving all active subscriptions to user tracking change notifications.", + "produces": [ + "application/json" + ], + "responses": { + "200": { + "description": "Response to retrieve user tracking subscriptions", + "schema": { + "type": "object", + "properties": { + "notificationSubscriptionList": { + "type": "object", + "properties": { + "userTrackingSubscription": { + "type": "array", + "items": { + "$ref": "#/definitions/UserTrackingSubscription" + } + }, + "resourceURL": { + "$ref": "#/definitions/ResourceURL" + } + } + } + } + }, + "examples": { + "application/json": { + "notificationSubscriptionList": { + "userTrackingSubscription": [ + { + "clientCorrelator": "0123", + "resourceURL": "http://example.com/exampleAPI/location/v1/subscriptions/userTracking/subscription123", + "callbackReference": { + "notifyURL": "http://clientApp.example.com/location_notifications/123456" + }, + "address": "acr:192.0.2.1", + "userEventCriteria": "Transferring" + }, + { + "clientCorrelator": "0124", + "resourceURL": "http://example.com/exampleAPI/location/v1/subscriptions/userTracking/subscription124", + "callbackReference": { + "notifyURL": "http://clientApp.example.com/location_notifications/123456" + }, + "address": "acr:192.0.2.2", + "userEventCriteria": "Transferring" + } + ], + "resourceURL": "http://example.com/exampleAPI/location/v1/subscriptions/userTracking" + } + } + } + } + } + }, + "post": { + "tags": [ + "subscriptions" + ], + "operationId": "userTrackingSubPost", + "description": "This operation is used for creating a new subscription to user tracking change notification", + "produces": [ + "application/json" + ], + "parameters": [ + { + "$ref": "#/parameters/Body.UserTrackingSubscription" + } + ], + "responses": { + "201": { + "description": "Response to create new user tracking subscription", + "schema": { + "properties": { + "userTrackingSubscription": { + "$ref": "#/definitions/UserTrackingSubscription" + } + } + }, + "examples": { + "application/json": { + "userTrackingSubscription": { + "clientCorrelator": "0123", + "resourceURL": "http://example.com/exampleAPI/location/v1/subscriptions/userTracking/subscription123", + "callbackReference": { + "notifyURL": "http://clientApp.example.com/location_notifications/123456" + }, + "address": "acr:192.0.2.1", + "userEventCriteria": "Transferring" + } + } + } + } + } + } + }, + "/subscriptions/userTracking/{subscriptionId}": { + "parameters": [ + { + "$ref": "#/parameters/Path.SubscriptionId" + } + ], + "get": { + "tags": [ + "subscriptions" + ], + "operationId": "userTrackingSubGetById", + "description": "This operation is used for retrieving an individual subscription to user tracking change notification.", + "produces": [ + "application/json" + ], + "responses": { + "200": { + "description": "Response to retrieve individual user tracking subscription", + "schema": { + "properties": { + "userTrackingSubscription": { + "$ref": "#/definitions/UserTrackingSubscription" + } + } + }, + "examples": { + "application/json": { + "userTrackingSubscription": { + "clientCorrelator": "0123", + "resourceURL": "http://example.com/exampleAPI/location/v1/subscriptions/userTracking/subscription123", + "callbackReference": { + "notifyURL": "http://clientApp.example.com/location_notifications/123456" + }, + "address": "acr:192.0.2.1", + "userEventCriteria": "Transferring" + } + } + } + } + } + }, + "put": { + "tags": [ + "subscriptions" + ], + "operationId": "userTrackingSubPutById", + "description": "This operation is used for updating an individual subscription to user tracking change notification.", + "produces": [ + "application/json" + ], + "parameters": [ + { + "$ref": "#/parameters/Body.UserTrackingSubscription" + } + ], + "responses": { + "200": { + "description": "Response to update individual user tracking subscription", + "schema": { + "properties": { + "userTrackingSubscription": { + "$ref": "#/definitions/UserTrackingSubscription" + } + } + }, + "examples": { + "application/json": { + "userTrackingSubscription": { + "clientCorrelator": "0123", + "resourceURL": "http://example.com/exampleAPI/location/v1/subscriptions/userTracking/subscription123", + "callbackReference": { + "notifyURL": "http://clientApp.example.com/location_notifications/123456" + }, + "address": "acr:192.0.2.1", + "userEventCriteria": "Transferring" + } + } + } + } + } + }, + "delete": { + "tags": [ + "subscriptions" + ], + "operationId": "userTrackingSubDelById", + "description": "This operation is used for retrieving an individual subscription to user tracking change notification.", + "produces": [ + "application/json" + ], + "responses": { + "204": { + "description": "No Content" + } + } + } + }, + "/subscriptions/zonalStatus": { + "get": { + "tags": [ + "subscriptions" + ], + "operationId": "zoneStatusGet", + "description": "This operation is used for creating a new subscription to zone status change notification.", + "produces": [ + "application/json" + ], + "responses": { + "200": { + "description": "Response to retrieve zone status subscriptions", + "schema": { + "type": "object", + "properties": { + "notificationSubscriptionList": { + "type": "object", + "properties": { + "zonalTrafficSubscription": { + "type": "array", + "items": { + "$ref": "#/definitions/ZoneStatusSubscription" + } + }, + "resourceURL": { + "$ref": "#/definitions/ResourceURL" + } + } + } + } + }, + "examples": { + "application/json": { + "notificationSubscriptionList": { + "zoneStatusSubscription": [ + { + "clientCorrelator": "0123", + "resourceURL": "http://example.com/exampleAPI/location/v1/subscriptions/zoneStatus/subscription123", + "callbackReference": { + "notifyURL": "http://clientApp.example.com/location_notifications/123456" + }, + "zoneId": "zone01", + "numberOfUsersZoneThreshold": "500", + "operationStatus": "Serviceable" + }, + { + "clientCorrelator": "0124", + "resourceURL": "http://example.com/exampleAPI/location/v1/subscriptions/zoneStatus/subscription124", + "callbackReference": { + "notifyURL": "http://clientApp.example.com/location_notifications/123457" + }, + "zoneId": "zone02", + "numberOfUsersAPThreshold": "50", + "operationStatus": "Serviceable" + } + ], + "resourceURL": "http://example.com/exampleAPI/location/v1/subscriptions/zoneStatus" + } + } + } + } + } + }, + "post": { + "tags": [ + "subscriptions" + ], + "operationId": "zoneStatusPost", + "description": "This operation is used for creating a new subscription to zone status change notification.", + "produces": [ + "application/json" + ], + "parameters": [ + { + "$ref": "#/parameters/Body.ZoneStatusSubscription" + } + ], + "responses": { + "201": { + "description": "Response to create new zone status subscription", + "schema": { + "properties": { + "zonalTrafficSubscription": { + "$ref": "#/definitions/ZoneStatusSubscription" + } + } + }, + "examples": { + "application/json": { + "zoneStatusSubscription": { + "clientCorrelator": "0123", + "resourceURL": "http://example.com/exampleAPI/location/v1/subscriptions/zoneStatus/subscription123", + "callbackReference": { + "notifyURL": "http://clientApp.example.com/location_notifications/123456" + }, + "zoneId": "zone01", + "numberOfUsersZoneThreshold": "500", + "operationStatus": "Serviceable" + } + } + } + } + } + } + }, + "/subscriptions/zoneStatus/{subscriptionId}": { + "parameters": [ + { + "$ref": "#/parameters/Path.SubscriptionId" + } + ], + "get": { + "tags": [ + "subscriptions" + ], + "operationId": "zoneStatusGetById", + "description": "This operation is used for retrieving an individual subscription to zone status change notification.", + "produces": [ + "application/json" + ], + "responses": { + "200": { + "description": "Response to retrieve individual zone status subscription", + "schema": { + "properties": { + "zoneStatusSubscription": { + "$ref": "#/definitions/ZoneStatusSubscription" + } + } + }, + "examples": { + "application/json": { + "zoneStatusSubscription": { + "clientCorrelator": "0123", + "resourceURL": "http://example.com/exampleAPI/location/v1/subscriptions/zoneStatus/subscription123", + "callbackReference": { + "notifyURL": "http://clientApp.example.com/location_notifications/123456" + }, + "zoneId": "zone01", + "numberOfUsersZoneThreshold": "500", + "operationStatus": "Serviceable" + } + } + } + } + } + }, + "put": { + "tags": [ + "subscriptions" + ], + "operationId": "zoneStatusPutById", + "description": "This operation is used for updating an individual subscription to zone status change notification.", + "produces": [ + "application/json" + ], + "parameters": [ + { + "$ref": "#/parameters/Body.ZoneStatusSubscription" + } + ], + "responses": { + "200": { + "description": "Response to update individual zone status subscription", + "schema": { + "properties": { + "zoneStatusSubscription": { + "$ref": "#/definitions/ZoneStatusSubscription" + } + } + }, + "examples": { + "application/json": { + "zoneStatusSubscription": { + "clientCorrelator": "0123", + "resourceURL": "http://example.com/exampleAPI/location/v1/subscriptions/zoneStatus/subscription123", + "callbackReference": { + "notifyURL": "http://clientApp.example.com/location_notifications/123456" + }, + "zoneId": "zone01", + "numberOfUsersZoneThreshold": "500", + "operationStatus": "Serviceable" + } + } + } + } + } + }, + "delete": { + "tags": [ + "subscriptions" + ], + "operationId": "zoneStatusDelById", + "description": "This operation is used for cancelling a subscription and stopping corresponding notifications.", + "produces": [ + "application/json" + ], + "responses": { + "204": { + "description": "No content" + } + } + } + } + }, + "definitions": { + "AccessPointId": { + "description": "Identifier of access point, (reference ETSI TS 129 171). Where the E-CGI is made up of the PLMN and Cell Identity (28 bit string). Then the PLMN is made up of the 3 digit MCC & 2 or 3 digit MNC. The Cell Portion is an optional element", + "type": "string", + "example": "001010000000000000000000000000001" + }, + "AccessPointInfo": { + "description": "A type containing access point information.", + "type": "object", + "required": [ + "accessPointId", + "connectionType", + "operationStatus", + "numberOfUsers", + "resourceURL" + ], + "properties": { + "accessPointId": { + "$ref": "#/definitions/AccessPointId" + }, + "locationInfo": { + "$ref": "#/definitions/LocationInfo" + }, + "connectionType": { + "$ref": "#/definitions/ConnectionType" + }, + "operationStatus": { + "$ref": "#/definitions/OperationStatus" + }, + "numberOfUsers": { + "$ref": "#/definitions/NumberOfUsers" + }, + "timezone": { + "$ref": "#/definitions/Timezone" + }, + "interestRealm": { + "$ref": "#/definitions/InterestRealm" + }, + "resourceURL": { + "$ref": "#/definitions/ResourceURL" + } + } + }, + "AccessPointList": { + "description": "A type containing list of access points.", + "type": "object", + "required": [ + "zoneId", + "resourceURL" + ], + "properties": { + "zoneId": { + "$ref": "#/definitions/ZoneId" + }, + "accessPoint": { + "description": "Collection of the access point information list.", + "type": "array", + "items": { + "$ref": "#/definitions/AccessPointInfo" + } + }, + "resourceURL": { + "$ref": "#/definitions/ResourceURL" + } + } + }, + "Address": { + "description": "Address of user (e.g. \"sip\" URI, \"tel\" URI, \"acr\" URI).", + "type": "string", + "format": "uri", + "example": "acr:192.0.2.1" + }, + "AncillaryInfo": { + "description": "Reserved for future use.", + "type": "string" + }, + "CallbackData": { + "description": "CallBackData if passed by the application during the associated ZonalTrafficSubscription and UserTrackingSubscription operation. See [REST_NetAPI_Common].", + "type": "string", + "example": "1234" + }, + "CallbackReference": { + "description": "Notification callback definition.", + "type": "object", + "required": [ + "notifyURL" + ], + "properties": { + "notifyURL": { + "$ref": "#/definitions/NotifyURL" + } + } + }, + "ClientCorrelator": { + "description": "Uniquely identifies this create subscription request. If there is a communication failure during the request, using the same clientCorrelator when retrying the request allows the operator to avoid creating a duplicate subscription.", + "type": "string", + "example": "0123" + }, + "ConnectionType": { + "description": "The connection type for the access point", + "type": "string", + "enum": [ + "Femto", + "LTE-femto", + "Smallcell", + "LTE-smallcell", + "Wifi", + "Pico", + "Micro", + "Macro", + "Wimax", + "Unknown" + ], + "example": "Macro" + }, + "ContextLocationInfo": { + "description": "Contextual information of a user location (e.g., aisle, floor, room number, etc.)", + "type": "string", + "example": "GroundFloor" + }, + "CurrentAccessPointId": { + "description": "Zone ID", + "type": "string", + "example": "zone01" + }, + "Duration": { + "description": "Period (in seconds) of time notifications are provided for. If set to \"0\" (zero), a default duration time, which is specified by the service policy, will be used. If the parameter is omitted, the notifications will continue until the maximum duration time, which is specified by the service policy, unless the notifications are stopped by deletion of subscription for notifications. This element MAY be given by the client during resource creation in order to signal the desired lifetime of the subscription. The server MUST return in this element the period of time for which the subscription will still be valid.", + "type": "string", + "example": "0" + }, + "InterestRealm": { + "description": "Interest realm of access point (e.g. geographical area, a type of industry etc.).", + "type": "string", + "example": "LA" + }, + "Link": { + "description": "Link to other resources", + "type": "object", + "required": [ + "rel", + "href" + ], + "properties": { + "rel": { + "description": "Describes the relationship between the URI and the resource.", + "type": "object", + "format": "string" + }, + "href": { + "description": "URI", + "type": "object", + "format": "anyURI" + } + } + }, + "LocationInfo": { + "description": "A type containing location information with latitude, longitude and altitude, in addition the accuracy of the information are provided.", + "type": "object", + "required": [ + "latitude", + "longitude", + "accuracy" + ], + "properties": { + "latitude": { + "type": "number", + "format": "float", + "example": "80.123" + }, + "longitude": { + "type": "number", + "format": "float", + "example": "70.123" + }, + "altitude": { + "type": "number", + "format": "float", + "example": "10.0" + }, + "accuracy": { + "type": "integer", + "format": "int32", + "example": "10" + } + } + }, + "NotifyURL": { + "description": "The URL of your own listener application.", + "type": "string", + "format": "url", + "example": "http://clientApp.example.com/location_notifications/123456" + }, + "NumberOfAccessPoints": { + "description": "The number of access points within the zone", + "type": "integer", + "format": "uint32", + "example": "10" + }, + "NumberOfUnserviceableAccessPoints": { + "description": "Number of inoperable access points within the zone.", + "type": "integer", + "format": "uint32", + "example": "9" + }, + "NumberOfUsers": { + "description": "The number of users currently on the access point.", + "type": "integer", + "format": "uint32", + "example": "7" + }, + "NumberOfUsersAPThreshold": { + "description": "Threshold number of users in an access point which if crossed shall cause a notification.", + "type": "integer", + "format": "uint32", + "example": "20" + }, + "NumberOfUsersInAP": { + "description": "This element shall be present when ZoneStatusSubscription includes numberOfUsersAPThreshold element and the number of users in an access point exceeds the threshold defined in the subscription.", + "type": "integer", + "format": "uint32", + "example": "12" + }, + "NumberOfUsersInZone": { + "description": "This element shall be present when ZoneStatusSubscription includes numberOfUsersZoneThreshold element and the number of users in a zone exceeds the threshold defined in this subscription.", + "type": "integer", + "format": "uint32", + "example": "20" + }, + "NumberOfUsersZoneThreshold": { + "description": "Threshold number of users in a zone which if crossed shall cause a notification.", + "type": "integer", + "format": "uint32", + "example": "40" + }, + "OperationStatus": { + "description": "The operation status of the access point", + "type": "string", + "enum": [ + "Serviceable", + "Unserviceable", + "Unknown" + ], + "example": "Serviceable" + }, + "PreviousAccessPointId": { + "description": "Zone ID", + "type": "string", + "example": "zone02" + }, + "ResourceURL": { + "description": "Self referring URL.", + "type": "string", + "format": "uri", + "example": "http://example.com/exampleAPI/location/v1/subscriptions/userTracking/subscription123" + }, + "Timestamp": { + "description": "Indicates the time of day for zonal presence notification.", + "type": "string", + "format": "date-time", + "example": "2017-01-01T02:51:43Z" + }, + "Timezone": { + "description": "Time zone of access point", + "type": "string", + "format": "date-time", + "example": "2017-01-01T02:51:43Z" + }, + "UserEventCriteria": { + "description": "List of user event values to generate notifications for (these apply to address specified). If this element is missing, a notification is requested to be generated for any change in user event.", + "type": "array", + "items": { + "$ref": "#/definitions/UserEventType" + } + }, + "UserEventType": { + "description": "User event", + "type": "string", + "enum": [ + "Entering", + "Leaving", + "Transferring" + ], + "example": "Entering" + }, + "UserInfo": { + "description": "A type containing user information.", + "type": "object", + "required": [ + "address", + "accessPointId", + "zoneId", + "resourceURL" + ], + "properties": { + "address": { + "$ref": "#/definitions/Address" + }, + "accessPointId": { + "$ref": "#/definitions/AccessPointId" + }, + "zoneId": { + "$ref": "#/definitions/ZoneId" + }, + "resourceURL": { + "$ref": "#/definitions/ResourceURL" + }, + "locationInfo": { + "$ref": "#/definitions/LocationInfo" + }, + "contextLocationInfo": { + "$ref": "#/definitions/ContextLocationInfo" + }, + "ancillaryInfo": { + "$ref": "#/definitions/AncillaryInfo" + } + } + }, + "UserList": { + "description": "A type containing list of users.", + "type": "object", + "required": [ + "resourceURL" + ], + "properties": { + "user": { + "description": "Collection of the zone information list.", + "type": "array", + "items": { + "$ref": "#/definitions/UserInfo" + } + }, + "resourceURL": { + "$ref": "#/definitions/ResourceURL" + } + } + }, + "UserTrackingSubscription": { + "description": "A type containing user tracking subscription.", + "type": "object", + "required": [ + "callbackReference", + "address" + ], + "properties": { + "clientCorrelator": { + "$ref": "#/definitions/ClientCorrelator" + }, + "callbackReference": { + "$ref": "#/definitions/CallbackReference" + }, + "address": { + "$ref": "#/definitions/Address" + }, + "userEventCriteria": { + "$ref": "#/definitions/UserEventCriteria" + }, + "resourceURL": { + "$ref": "#/definitions/ResourceURL" + } + } + }, + "ZonalPresenceNotification": { + "description": "A type containing zonal presence notification", + "type": "object", + "required": [ + "zoneId", + "address", + "userEventType", + "currentAccessPointId", + "timestamp" + ], + "properties": { + "callbackData": { + "$ref": "#/definitions/CallbackData" + }, + "zoneId": { + "$ref": "#/definitions/ZoneId" + }, + "address": { + "$ref": "#/definitions/Address" + }, + "interestRealm": { + "$ref": "#/definitions/InterestRealm" + }, + "userEventType": { + "$ref": "#/definitions/UserEventType" + }, + "currentAccessPointId": { + "$ref": "#/definitions/CurrentAccessPointId" + }, + "previousAccessPointId": { + "$ref": "#/definitions/PreviousAccessPointId" + }, + "timestamp": { + "$ref": "#/definitions/Timestamp" + }, + "link": { + "description": "Link to other resources that are in relationship with this notification. The server SHOULD include a link to the related subscription. No other links are required or suggested by this specification.", + "type": "array", + "items": { + "$ref": "#/definitions/Link" + }, + "example": "rel=\"ZonalTrafficSubscription\" href=\"http://example.com/exampleAPI/location/v1/subscriptions/zonalTraffic/sub123\"/" + } + } + }, + "ZonalTrafficSubscription": { + "description": "A type containing zonal traffic subscription", + "type": "object", + "required": [ + "callbackReference", + "zoneId" + ], + "properties": { + "clientCorrelator": { + "$ref": "#/definitions/ClientCorrelator" + }, + "callbackReference": { + "$ref": "#/definitions/CallbackReference" + }, + "zoneId": { + "$ref": "#/definitions/ZoneId" + }, + "interestRealm": { + "description": "Interest realms of access points within a zone (e.g. geographical area, a type of industry etc.).", + "type": "array", + "items": { + "$ref": "#/definitions/InterestRealm" + } + }, + "userEventCriteria": { + "description": "List of user event values to generate notifications for (these apply to zone identifier or all interest realms within zone identifier specified). If this element is missing, a notification is requested to be generated for any change in user event.", + "type": "array", + "items": { + "$ref": "#/definitions/UserEventType" + } + }, + "duration": { + "$ref": "#/definitions/Duration" + }, + "resourceURL": { + "$ref": "#/definitions/ResourceURL" + } + } + }, + "ZoneId": { + "description": "Identifier of zone", + "type": "string", + "example": "zone01" + }, + "ZoneInfo": { + "description": "A type containing zone information.", + "type": "object", + "required": [ + "zoneId", + "numberOfAccessPoints", + "numberOfUnservicableAccessPoints", + "numberOfUsers", + "resourceURL" + ], + "properties": { + "zoneId": { + "$ref": "#/definitions/ZoneId" + }, + "numberOfAccessPoints": { + "$ref": "#/definitions/NumberOfAccessPoints" + }, + "numberOfUnservicableAccessPoints": { + "$ref": "#/definitions/NumberOfUnserviceableAccessPoints" + }, + "numberOfUsers": { + "$ref": "#/definitions/NumberOfUsers" + }, + "resourceURL": { + "$ref": "#/definitions/ResourceURL" + } + } + }, + "ZoneList": { + "description": "Collection of the zone information list.", + "type": "object", + "required": [ + "resourceURL" + ], + "properties": { + "zone": { + "description": "Collection of the zone information list.", + "type": "array", + "items": { + "$ref": "#/definitions/ZoneInfo" + } + }, + "resourceURL": { + "$ref": "#/definitions/ResourceURL" + } + } + }, + "ZoneStatusNotification": { + "description": "A type containing zone status notification.", + "type": "object", + "required": [ + "zoneId", + "timestamp" + ], + "properties": { + "callbackData": { + "$ref": "#/definitions/CallbackData" + }, + "zoneId": { + "$ref": "#/definitions/ZoneId" + }, + "accessPointId": { + "$ref": "#/definitions/AccessPointId" + }, + "numberOfUsersInZone": { + "$ref": "#/definitions/NumberOfUsersInZone" + }, + "numberOfUsersInAP": { + "$ref": "#/definitions/NumberOfUsersInAP" + }, + "operationStatus": { + "$ref": "#/definitions/OperationStatus" + }, + "timestamp": { + "$ref": "#/definitions/Timestamp" + }, + "link": { + "description": "Link to other resources that are in relationship with this notification. The server SHOULD include a link to the related subscription. No other links are required or suggested by this specification.", + "type": "array", + "items": { + "$ref": "#/definitions/Link" + }, + "example": "rel=\"ZonalStatusSubscription\" href=\"http://example.com/exampleAPI/location/v1/subscriptions/zonalStatus/sub123\"" + } + } + }, + "ZoneStatusSubscription": { + "description": "A type containing zone status subscription.", + "type": "object", + "required": [ + "callbackReference", + "zoneId" + ], + "properties": { + "clientCorrelator": { + "$ref": "#/definitions/ClientCorrelator" + }, + "resourceURL": { + "$ref": "#/definitions/ResourceURL" + }, + "callbackReference": { + "$ref": "#/definitions/CallbackReference" + }, + "zoneId": { + "$ref": "#/definitions/ZoneId" + }, + "numberOfUsersZoneThreshold": { + "$ref": "#/definitions/NumberOfUsersZoneThreshold" + }, + "numberOfUsersAPThreshold": { + "$ref": "#/definitions/NumberOfUsersAPThreshold" + }, + "operationStatus": { + "description": "List of operation status values to generate notifications for (these apply to all access points within a zone).", + "type": "array", + "items": { + "$ref": "#/definitions/OperationStatus" + } + } + } + } + } +} diff --git a/ttcn/LibMec/LocationAPI/ttcn/LocationAPI_EncdecDeclarations.ttcn b/ttcn/LibMec/LocationAPI/ttcn/LocationAPI_EncdecDeclarations.ttcn new file mode 100644 index 0000000..1a41826 --- /dev/null +++ b/ttcn/LibMec/LocationAPI/ttcn/LocationAPI_EncdecDeclarations.ttcn @@ -0,0 +1,15 @@ +module LocationAPI_EncdecDeclarations { + + import from LocationAPI_TypesAndValues all; + + external function enc_UserInfo(in UserInfo p_user_info) return octetstring + with { extension "prototype (convert) encode(JSON)"; extension "printing(pretty)" }; + external function dec_UserInfo(in octetstring p_pdu) return UserInfo + with { extension "prototype (convert) decode(JSON)" }; + + external function enc_LocationInfo(in LocationInfo p_location_info) return octetstring + with { extension "prototype (convert) encode(JSON)"; extension "printing(pretty)" }; + external function dec_LocationInfo(in octetstring p_pdu) return LocationInfo + with { extension "prototype (convert) decode(JSON)" }; + +} // End of module LocationAPI_EncdecDeclarations diff --git a/ttcn/LibMec/LocationAPI/ttcn/LocationAPI_Pics.ttcn b/ttcn/LibMec/LocationAPI/ttcn/LocationAPI_Pics.ttcn new file mode 100644 index 0000000..da5448b --- /dev/null +++ b/ttcn/LibMec/LocationAPI/ttcn/LocationAPI_Pics.ttcn @@ -0,0 +1,5 @@ +module LocationAPI_Pics { + + modulepar boolean PICS_LOCATION_API_SUPPORTED := true; + +} // End of module LocationAPI_Pics diff --git a/ttcn/LibMec/LocationAPI/ttcn/LocationAPI_Pixits.ttcn b/ttcn/LibMec/LocationAPI/ttcn/LocationAPI_Pixits.ttcn new file mode 100644 index 0000000..abbb123 --- /dev/null +++ b/ttcn/LibMec/LocationAPI/ttcn/LocationAPI_Pixits.ttcn @@ -0,0 +1,25 @@ +module LocationAPI_Pixits { + + // LibCommon + import from LibCommon_BasicTypesAndValues all; + + // LibMec/LocationAPI + import from LocationAPI_TypesAndValues all; + + modulepar universal charstring PX_ADDRESS := "acr:127.0.0.1"; + + modulepar universal charstring PX_ACCESS_POINT_ID := "001010000000000000000000000000001"; + + modulepar universal charstring PX_ZONE_ID := "zone01"; + + modulepar universal charstring PX_RESOURCE_URL := "http://example.com/exampleAPI/location/v2/zones/zone01"; + + modulepar TimeStamp PX_TIME_STAMP := { seconds := 1483231138, nanoSeconds := 0 }; + + modulepar UInt32 PX_NB_ACCESS_POINTS := 3; + + modulepar UInt32 PX_NB_UNSERVICABLEL_ACCESS_POINTS := 1; + + modulepar UInt32 PX_NB_USERS := 10; + +} // End of module LocationAPI_Pixits diff --git a/ttcn/LibMec/LocationAPI/ttcn/LocationAPI_Templates.ttcn b/ttcn/LibMec/LocationAPI/ttcn/LocationAPI_Templates.ttcn new file mode 100644 index 0000000..1b07b08 --- /dev/null +++ b/ttcn/LibMec/LocationAPI/ttcn/LocationAPI_Templates.ttcn @@ -0,0 +1,94 @@ +module LocationAPI_Templates { + + // LibCommon + import from LibCommon_BasicTypesAndValues all; + + // LibMec/LocationAPI + import from LocationAPI_TypesAndValues all; + import from LocationAPI_Pixits all; + + template (value) UserInfo m_user_info( + in universal charstring p_address := PX_ADDRESS, + in universal charstring p_accessPointId := PX_ACCESS_POINT_ID, + in universal charstring p_zoneId := PX_ZONE_ID, + in universal charstring p_resourceURL := PX_RESOURCE_URL, + in TimeStamp p_timeStamp := PX_TIME_STAMP + ) := { + address_ := p_address, + accessPointId := p_accessPointId, + zoneId := p_zoneId, + resourceURL := p_resourceURL, + timeStamp := p_timeStamp, + locationInfo := omit, + contextLocationInfo := omit, + ancillaryInfo := omit + } // End of template m_user_info + + template (present) UserInfo mw_user_info( + template (present) universal charstring p_address := ?, + template (present) universal charstring p_accessPointId := ?, + template (present) universal charstring p_zoneId := ?, + template (present) universal charstring p_resourceURL := ?, + template (present) TimeStamp p_timeStamp := ? + ) := { + address_ := p_address, + accessPointId := p_accessPointId, + zoneId := p_zoneId, + resourceURL := p_resourceURL, + timeStamp := p_timeStamp, + locationInfo := *, + contextLocationInfo := *, + ancillaryInfo := * + } // End of template mw_user_info + + template (value) LocationInfo m_location_info( + in float p_latitude, + in float p_longitude, + in integer p_accuracy + ) := { + latitude := p_latitude, + longitude := p_longitude, + altitude := omit, + accuracy := p_accuracy + } // End of template m_location_info + + template (present) LocationInfo mw_location_info( + template (present) float p_latitude := ?, + template (present) float p_longitude := ?, + template (present) integer p_accuracy := ? + ) := { + latitude := p_latitude, + longitude := p_longitude, + altitude := *, + accuracy := p_accuracy + } // End of template mw_location_info + + template (value) ZoneInfo m_zone_info( + in universal charstring p_zoneId := PX_ZONE_ID, + in UInt32 p_numberOfAccessPoints := PX_NB_ACCESS_POINTS, + in UInt32 p_numberOfUnservicableAccessPoints := PX_NB_UNSERVICABLEL_ACCESS_POINTS, + in UInt32 p_numberOfUsers := PX_NB_USERS, + in universal charstring p_resourceURL := PX_RESOURCE_URL + ) := { + zoneId := p_zoneId, + numberOfAccessPoints := p_numberOfAccessPoints, + numberOfUnservicableAccessPoints := p_numberOfUnservicableAccessPoints, + numberOfUsers := p_numberOfUsers, + resourceURL := p_resourceURL + } // End of template m_zone_info + + template (present) ZoneInfo mw_zone_info( + template (present) universal charstring p_zoneId := ?, + template (present) UInt32 p_numberOfAccessPoints := ?, + template (present) UInt32 p_numberOfUnservicableAccessPoints := ?, + template (present) UInt32 p_numberOfUsers := ?, + template (present) universal charstring p_resourceURL := ? + ) := { + zoneId := p_zoneId, + numberOfAccessPoints := p_numberOfAccessPoints, + numberOfUnservicableAccessPoints := p_numberOfUnservicableAccessPoints, + numberOfUsers := p_numberOfUsers, + resourceURL := p_resourceURL + } // End of template mw_zone_info + +} // End of module LocationAPI_Templates diff --git a/ttcn/LibMec/LocationAPI/ttcn/LocationAPI_TypesAndValues.ttcn b/ttcn/LibMec/LocationAPI/ttcn/LocationAPI_TypesAndValues.ttcn new file mode 100644 index 0000000..9bced74 --- /dev/null +++ b/ttcn/LibMec/LocationAPI/ttcn/LocationAPI_TypesAndValues.ttcn @@ -0,0 +1,50 @@ +module LocationAPI_TypesAndValues { + + // LibCommon + import from LibCommon_BasicTypesAndValues all; + + type record TimeStamp { + UInt32 seconds, + UInt32 nanoSeconds + } + + /** + * @desc A type containing user information. + */ + type record UserInfo { + universal charstring address_, + universal charstring accessPointId, + universal charstring zoneId, + universal charstring resourceURL, + TimeStamp timeStamp, + LocationInfo locationInfo optional, + universal charstring contextLocationInfo optional, + universal charstring ancillaryInfo optional + } with { + variant (address_) "name as 'address'"; + } // End of type UserInfo + + /** + * @desc A type containing location information with latitude, longitude and altitude, in addition the accuracy of the information are provided. + */ + type record LocationInfo { + float latitude, + float longitude, + float altitude optional, + integer accuracy + } // End of type LocationInfo + + /** + * @desc A type containing zone information. + */ + type record ZoneInfo { + universal charstring zoneId, + UInt32 numberOfAccessPoints, + UInt32 numberOfUnservicableAccessPoints, + UInt32 numberOfUsers, + universal charstring resourceURL + } + +} with { + encode "JSON" +} diff --git a/ttcn/LibMec/ttcn/LibMec_Functions.ttcn b/ttcn/LibMec/ttcn/LibMec_Functions.ttcn new file mode 100644 index 0000000..3e71376 --- /dev/null +++ b/ttcn/LibMec/ttcn/LibMec_Functions.ttcn @@ -0,0 +1,111 @@ +module LibMec_Functions { + + // Libcommon + import from LibCommon_Time all; + import from LibCommon_VerdictControl all; + import from LibCommon_Sync all; + + // LibHttp + import from LibItsHttp_TypesAndValues all; + import from LibItsHttp_Templates all; + import from LibItsHttp_TestSystem all; + + group preambles { + + /** + * @desc Setup HTTP pprotocol port + */ + function f_cf_01_http_up() runs on HttpComponent { + + // Map ports + map(self:httpPort, system:httpPort); + + // Connect + f_connect4SelfOrClientSync(); + + activate(a_cf_01_http_down()); + activate(a_default_requests()); + activate(a_default_responses()); + + } // End of function f_cf_01_http_up + + } // End of group preambles + + group postambles { + + /** + * @desc Shutdown HTTP pprotocol port + */ + function f_cf_01_http_down() runs on HttpComponent { + + // Unmap ports + unmap(self:httpPort, system:httpPort); + + // Disconnect ports + f_disconnect4SelfOrClientSync(); + + deactivate; + } // End of function f_cf_01_http_down + + /** + * @desc Default handling cf01 de-initialisation. + */ + altstep a_cf_01_http_down() runs on HttpComponent { + [] a_shutdown() { + f_cf_01_http_down(); + log("*** a_cf_01_http_down: INFO: TEST COMPONENT NOW STOPPING ITSELF! ***"); + stop; + } + } // End of altstep a_cf_01_http_down + + } // End of group postambles + + group altsteps { + + altstep a_default_requests() runs on HttpComponent { + [] httpPort.receive(mw_http_request) { + tc_ac.stop; + log("*** " & testcasename() & ": FAIL: Server error: Receive request istead of response ***"); + f_selfOrClientSyncAndVerdictTestBody(c_tbDone, e_error); + } + } // End of altstep a_default_requests + + altstep a_default_responses() runs on HttpComponent { + var HttpMessage v_response; + + [] httpPort.receive( + mw_http_response( + mw_http_response_ok( + mw_http_message_body_xml + ))) { + tc_ac.stop; + log("*** " & testcasename() & ": FAIL: Unexpected XML response ***"); + f_selfOrClientSyncAndVerdictTestBody(c_tbDone, e_error); + } + [] httpPort.receive( + mw_http_response( + mw_http_response_ok( + mw_http_message_body_binary + ))) { + tc_ac.stop; + log("*** " & testcasename() & ": FAIL: Unexpected binary response ***"); + f_selfOrClientSyncAndVerdictTestBody(c_tbDone, e_error); + } + [] httpPort.receive( + mw_http_response( + mw_http_response_ko + )) -> value v_response { + tc_ac.stop; + log("*** " & testcasename() & ": FAIL: Server error: " & int2str(v_response.response.statuscode) & "/" & v_response.response.statustext & " ***"); + f_selfOrClientSyncAndVerdictTestBody(c_tbDone, e_error); + } + [] httpPort.receive(mw_http_response) -> value v_response { + tc_ac.stop; + log("*** " & testcasename() & ": FAIL: Server error: " & int2str(v_response.response.statuscode) & "/" & v_response.response.statustext & " ***"); + f_selfOrClientSyncAndVerdictTestBody(c_tbDone, e_error); + } + } // End of altstep a_default_responses + + } // end of group altsteps + +} // End of module LibMec_Functions diff --git a/ttcn/LibMec/ttcn/LibMec_Pics.ttcn b/ttcn/LibMec/ttcn/LibMec_Pics.ttcn new file mode 100644 index 0000000..6597590 --- /dev/null +++ b/ttcn/LibMec/ttcn/LibMec_Pics.ttcn @@ -0,0 +1,10 @@ +module LibMec_Pics { + + /** + * @desc Does the IUT act as Edge Mobile Application? + */ + modulepar boolean PICS_ME_APP_IUT := false; + + modulepar charstring PICS_ME_APP_Q_ZONE_ID_URI := "/exampleAPI/location/v2/zones/"; + +} // End of module LibMec_Pics diff --git a/ttcn/LibMec/ttcn/LibMec_Pixits.ttcn b/ttcn/LibMec/ttcn/LibMec_Pixits.ttcn new file mode 100644 index 0000000..7f2d06b --- /dev/null +++ b/ttcn/LibMec/ttcn/LibMec_Pixits.ttcn @@ -0,0 +1,3 @@ +module LibMec_Pixits { + +} diff --git a/ttcn/TestCodec/TestCodec_External.ttcn b/ttcn/TestCodec/TestCodec_External.ttcn new file mode 100644 index 0000000..dfc2689 --- /dev/null +++ b/ttcn/TestCodec/TestCodec_External.ttcn @@ -0,0 +1,40 @@ +module TestCodec_External { + + // LibMec + import from LocationAPI_TypesAndValues all; + import from LocationAPI_Templates all; + + // TestCodec + import from TestCodec_TestAndSystem all; + + testcase tc_encode_LocationInfo() runs on TCType system TCType { + var LocationInfo v_location_info := valueof(m_location_info(10.0, 12.0, 9)); + var LocationInfo v_location_info_result; + var universal charstring v_result; + var universal charstring v_expected_result := "{\"latitude\":10.000000,\"longitude\":12.000000,\"accuracy\":9}"; + var bitstring v_enc_msg; + var integer v_res; + + v_enc_msg := encvalue(v_location_info); + v_result := oct2unichar(bit2oct(v_enc_msg), "UTF-8"); + log("v_result= ", v_result); + if (match(v_expected_result, v_result)) { + setverdict(pass, "Encoding succeed"); + } else { + setverdict(fail, "Encoding failed"); + } + + v_res := decvalue(v_enc_msg, v_location_info_result); + if (v_res == 0) { + log("v_location_info_result= ", v_location_info_result); + if (match(v_location_info, v_location_info_result)) { + setverdict(pass, "Decoding succeed"); + } else { + setverdict(fail, "Decoding failed"); + } + } else { + setverdict(fail, "Decoding operation failed"); + } + } // End of testcase tc_encode_LocationInfo + +} // End of module TestCodec_External diff --git a/ttcn/TestCodec/TestCodec_TestAndSystem.ttcn b/ttcn/TestCodec/TestCodec_TestAndSystem.ttcn new file mode 100644 index 0000000..4ba03ee --- /dev/null +++ b/ttcn/TestCodec/TestCodec_TestAndSystem.ttcn @@ -0,0 +1,9 @@ +module TestCodec_TestAndSystem { + + type component TCType { } + + type record TestRecord { + bitstring bs optional + } + +} // End of module TestCodec_TestAndSystem \ No newline at end of file diff --git a/ttcn/patch_lib_common_titan/LibCommon_Sync.ttcn b/ttcn/patch_lib_common_titan/LibCommon_Sync.ttcn new file mode 100644 index 0000000..38e29df --- /dev/null +++ b/ttcn/patch_lib_common_titan/LibCommon_Sync.ttcn @@ -0,0 +1,1358 @@ +/** + * @author ETSI + * @version $URL: https://oldforge.etsi.org/svn/LibCommon/tags/v1.4.0/ttcn/LibCommon_Sync.ttcn $ + * $Id: LibCommon_Sync.ttcn 66 2017-03-06 09:59:41Z filatov $ + * @desc This module implements _one_ generic synchronization mechanism + * for TTCN-3 test cases with one or more test components. + * Key concept is here that one test component acts as a + * synchronization server which listens and triggers one or more + * synchronization clients. It is recomended to use the MTC always as + * the synchronization server but in theory also a PTC can act as such + * a server.

+ * This synchronization is used by calling a function on + * the server test component to wait for a desired amount of clients + * to notify the server that they have reached a specific synchronization + * point. Each client test component must call another + * function to perform this notification.

+ * In the event that a client is not able to reach a synchronization + * point the server sends out a signal to all clients to abort the + * test case. This signal is a STOP message which can be caught by + * a test component default which in turn can then run a proper + * shut down behavior based on the current state of the test + * component.

+ * Note that this synchronization mechanism can also be used + * in a special mode called "self synchronization" when a test case + * only has one test component. Here, the test component in essence + * acts as a server and client at the same time. The main benefit of + * using self synchoronization is that the same shutdown mechanisms + * can also be reused fomr the multi component test cases.

+ * This module contains a lot of TTCN-3 definitions. It has been + * structured into tree main groups to help the user to identify + * quickly relevant TTCN-3 definitions. For rookie users of this + * module basicUserRelevantDefinitions should offer all the needed + * definitions. Advanced users can consider use of definitions in + * advancedUserRelevantDefinitions. Finally, internalDefinitions + * are definitions which are required for the module to work + * properly but do not need to be used in your code. Remember that + * the main motiviation of this sychronization module is to offer + * are _simple_ user interface. Practice has shown that when writing + * actual test component behavior _only a handful_ of functions + * usually wind up being used! Also check the synchronization examples + * module for example uses of this synchronization mechanism.

+ * The invocation of the sync functions is also closely tied + * to the verdict control functions which should also be reviewed + * prior to using this module.

+ * This module has been derived from EtsiCommon_Synchronization + * which was created in ETSIs STF256/276. It has been kept + * intentionally separate to avoid conflicts with future ETSI + * test suite releases. + * @see LibCommon_Sync.basicUserRelevantDefinitions + * @see LibCommon_Sync.advancedUserRelevantDefinitions + * @remark End users should be aware that any changes made to the in + * definitions this module may be overwritten in future releases. + * End users are encouraged to contact the distributers of this + * module regarding their modifications or additions so that future + * updates will include your changes. + * @copyright ETSI Copyright Notification + * No part may be reproduced except as authorized by written permission. + * The copyright and the foregoing restriction extend to reproduction in all media. + * All rights reserved. + * + */ +module LibCommon_Sync { + + //Common + import from LibCommon_BasicTypesAndValues { type UInt } ; + import from LibCommon_AbstractData all; + import from LibCommon_VerdictControl all; + + group basicUserRelevantDefinitions { + + group importantSyncTypeDefinitions { + + group compTypeRelated { + + /** + * @desc This type is used to be the base of any synchronization + * behavior which is to be executed on a sync server + * component. The test component which acts as a + * sync server in a test case must NOT directly use + * this component type in its runs on clause! + * Note that server synchronization functions may be + * invoked by a test component as long as its + * component type is type compatible to this component + * type definition! + */ + type component BaseSyncComp { + port SyncPort syncPort; + timer tc_sync := PX_TSYNC_TIME_LIMIT; + } + + /** + * @desc This type is used to define any synchronization + * behavior which is to be executed on a sync server + * component. The test component which acts as a + * sync server in a test case may - but does + * not have to - directly use this component type its + * runs on clause. + * Note that server synchronization functions may be + * invoked by a test component as long as its + * component type is type compatible to this component + * type definition! + */ + type component ServerSyncComp extends BaseSyncComp { + timer tc_shutDown := PX_TSHUT_DOWN_TIME_LIMIT; + } + + /** + * @desc This type is used to define any synchronization + * behavior which is to be executed on a sync client + * component. The test component(s) which act as a + * sync client in a test case may - but do not have + * to - directly use this component type their runs + * on clause. + * Note that server synchronization functions may be + * invoked by a test component as long as its + * component type is type compatible to this component + * type definition! + */ + type component ClientSyncComp extends BaseSyncComp { + var StringStack v_stateStack:= c_initStringStack; + var TestcaseStep vc_testcaseStep := e_preamble; + } + + /** + * @desc This type is used to define any synchronization + * behavior which is relevant to non-concurrent test + * cases. + * Note that self synchronization functions may be + * invoked by a test component as long as its + * component type is type compatible to this component + * type definition! + * Note also that this type is type compatible to the + * ClientSyncComp type so that shutdown altsteps from + * concurrent test cases can also be reused in single + * component test cases! + * @see LibCommon_Sync.ClientSyncComp + */ + type component SelfSyncComp extends ClientSyncComp { + port SyncPort syncSendPort; + } + + /** + * @desc This port type must be imported into test suites + * when defining test component types which are + * type compatible to a synchronization component + * type + * @see LibCommon_Sync.SelfSyncComp + * @see LibCommon_Sync.ServerSyncComp + * @see LibCommon_Sync.ClientSyncComp + */ + type port SyncPort message { + inout SyncCmd + } with { + extension "internal" + } + + + /** + * @desc Describes in which step of execution is the testcase + */ + type enumerated TestcaseStep { + e_preamble, + e_testBody, + e_postamble + } + + } // end compTypeRelated + + group standardSyncPointNames { + const charstring c_prDone := "preambleDone"; + const charstring c_poDone := "postambleDone"; + const charstring c_tbDone := "testBodyDone"; + const charstring c_initDone := "initDone"; + } + + } // end group importantSyncTypeDefinitions + + group syncCompTestConfiguration { + + /** + * @desc Calls self connect function if invoking + * component is the MTC or otherwise connects the client + * the server. This function allows to implement preambles + * in a way that they can be used by test components + * in both non-concurrent as well as concurrent test + * cases! + * @remark This function should _not_ be called if the MTC + * acts as a client (and not a server) in a concurrent + * test case. In this case f_connect4ClientSync + * should be used instead. + * @see LibCommon_Sync.f_connect4SelfSync + * @see LibCommon_Sync.f_connect4ClientSync + */ + function f_connect4SelfOrClientSync() + runs on SelfSyncComp { + if ( self == mtc ) { + f_connect4SelfSync(); + } else { + f_connect4ClientSync(); + } + } + + /** + * @desc Calls self connect function if the invoking + * component is the MTC or otherwise disconnects the client + * from the server. This function allows to implement + * postambles in a way that they can be used in both + * non-concurrent as well as concurrent test cases. + * @remark This function should _not_ be called if the MTC + * acts as a client (and not a server) in a concurrent + * test case. In this case f_disconnect4ClientSync + * should be used instead. + * @see LibCommon_Sync.f_disconnect4SelfSync + * @see LibCommon_Sync.f_disconnect4ClientSync + */ + function f_disconnect4SelfOrClientSync() + runs on SelfSyncComp { + if ( self == mtc ) { + f_disconnect4SelfSync(); + } else { + f_disconnect4ClientSync(); + } + } + + } // end group syncCompTestConfiguration + + group syncFunctions { + + /** + * @desc Implements synchronization of 2 clients from server side + * on one or more synchronization points. + * If problem occurs, then server sends STOP to all clients. + * Waits for PX_TSYNC_TIME_LIMIT to let clients + * finish executing their behavior until this + * synchronization point. After passing all synchronization + * points successfuly the server waits for all clients + * to stop. + * See f_serverSyncClientsTimed for overwriting this + * the timing constraint! + * This function sets the server component verdict. + * @remark The use of this function requires prior connection of + * the server sync ports! + * @see LibCommon_Sync.f_connect4SelfOrClientSync + * @see LibCommon_Sync.PX_TSYNC_TIME_LIMIT + * @see LibCommon_Sync.f_serverSyncClientsTimed + * @see LibCommon_Sync.f_serverWaitForAllClientsToStop + * @param p_syncPointIds list of synchronization point name/ids + */ + function f_serverSync2ClientsAndStop( in SyncPointList p_syncPointIds ) + runs on ServerSyncComp { + f_serverSyncNClientsAndStop(2, p_syncPointIds); + } + + /** + * @desc Implements synchronization of 3 clients from server side + * on one or more synchronization points. + * If problem occurs, then server sends STOP to all clients. + * Waits for PX_TSYNC_TIME_LIMIT to let clients + * finish executing their behavior until this + * synchronization point. After passing all synchronization + * points successfuly the server waits for all clients + * to stop. + * See f_serverSyncClientsTimed for overwriting this + * the timing constraint! + * This function sets the server component verdict. + * @remark The use of this function requires prior connection of + * the server sync ports! + * @see LibCommon_Sync.f_connect4SelfOrClientSync + * @see LibCommon_Sync.PX_TSYNC_TIME_LIMIT + * @see LibCommon_Sync.f_serverSyncClientsTimed + * @see LibCommon_Sync.f_serverWaitForAllClientsToStop + * @param p_syncPointIds list of synchronization point name/ids + */ + function f_serverSync3ClientsAndStop( in SyncPointList p_syncPointIds ) + runs on ServerSyncComp { + f_serverSyncNClientsAndStop(3, p_syncPointIds); + } + + /** + * @desc Implements synchronization of 4 clients from server side + * on one or more synchronization points. + * If problem occurs, then server sends STOP to all clients. + * Waits for PX_TSYNC_TIME_LIMIT to let clients + * finish executing their behavior until this + * synchronization point. After passing all synchronization + * points successfuly the server waits for all clients + * to stop. + * See f_serverSyncClientsTimed for overwriting this + * the timing constraint! + * This function sets the server component verdict. + * @remark The use of this function requires prior connection of + * the server sync ports! + * @see LibCommon_Sync.f_connect4SelfOrClientSync + * @see LibCommon_Sync.PX_TSYNC_TIME_LIMIT + * @see LibCommon_Sync.f_serverSyncClientsTimed + * @see LibCommon_Sync.f_serverWaitForAllClientsToStop + * @param p_syncPointIds list of synchronization point name/ids + */ + function f_serverSync4ClientsAndStop( in SyncPointList p_syncPointIds ) + runs on ServerSyncComp { + f_serverSyncNClientsAndStop(4, p_syncPointIds); + } + + /** + * @desc Implements synchronization of N clients from server side + * on one or more synchronization points. + * If problem occurs, then server sends STOP to all clients. + * Waits for PX_TSYNC_TIME_LIMIT to let clients + * finish executing their behavior until this + * synchronization point. After passing all synchronization + * points successfuly the server waits for all clients + * to stop. + * See f_serverSyncClientsTimed for overwriting this + * the timing constraint! + * This function sets the server component verdict. + * @remark The use of this function requires prior connection of + * the server sync ports! + * @see LibCommon_Sync.f_connect4SelfOrClientSync + * @see LibCommon_Sync.PX_TSYNC_TIME_LIMIT + * @see LibCommon_Sync.f_serverSyncClientsTimed + * @see LibCommon_Sync.f_serverWaitForAllClientsToStop + * @param p_numClients number of synchronization clients + * @param p_syncPointIds list of synchronization point name/ids + */ + function f_serverSyncNClientsAndStop ( + in UInt p_numClients, + in SyncPointList p_syncPointIds ) + runs on ServerSyncComp { + var integer i, v_noOfSyncIds := sizeof(p_syncPointIds); + for ( i := 0; i < v_noOfSyncIds; i := i+1 ) { + f_serverSyncClientsTimed ( + p_numClients, + valueof(p_syncPointIds[i]), + PX_TSYNC_TIME_LIMIT ); + } + f_serverWaitForAllClientsToStop(); + } + + /** + * @desc Implements synchronization of 2 clients and 1 UT from server side + * on one or more synchronization points. + * If problem occurs, then server sends STOP to all clients. + * Waits for PX_TSYNC_TIME_LIMIT to let clients + * finish executing their behavior until this + * synchronization point. After passing all synchronization + * points successfuly the server waits for all clients + * to stop. + * See f_serverSyncClientsTimed for overwriting this + * the timing constraint! + * This function sets the server component verdict. + * @remark The use of this function requires prior connection of + * the server sync ports! + * @see LibCommon_Sync.f_connect4SelfOrClientSync + * @see LibCommon_Sync.PX_TSYNC_TIME_LIMIT + * @see LibCommon_Sync.f_serverSyncClientsTimed + * @see LibCommon_Sync.f_serverWaitForAllClientsToStop + * @param p_syncPointIds list of synchronization point name/ids + */ + function f_serverSync2ClientsUtAndStop( in SyncPointList p_syncPointIds ) + runs on ServerSyncComp { + var integer i, v_noOfSyncIds := sizeof(p_syncPointIds); + for ( i := 0; i < v_noOfSyncIds; i := i+1 ) { + f_serverSyncClientsTimed(3,valueof(p_syncPointIds[i]), PX_TSYNC_TIME_LIMIT); + } + f_serverWaitForAllClientsToStop(); + } + + /** + * @desc Calls either self synchronization function if + * invoking component is the MTC, otherwise + * calls client synchronization. After that it + * sets the verdict based on the specified return code. + * This function allows to implement TTCN-3 functions + * in a way that they can be used in both non-concurrent + * as well as concurrent test cases. + * @remark This function should _not_ be called if the MTC + * acts as a client (and not a server) in a concurrent + * test case. In this case f_clientSyncAndVerdict + * should be used instead. + * @param p_syncPoint Synchronization point name/id + * @param p_ret Current behavior execution status + * @see LibCommon_Sync.f_clientSyncAndVerdict + * @see LibCommon_VerdictControl.f_setVerdict + */ + function f_selfOrClientSyncAndVerdict( in charstring p_syncPoint, + in FncRetCode p_ret) + runs on SelfSyncComp { + if ( self == mtc ) { + // then assume we are running non-conurrent test case + f_selfSyncAndVerdict(p_syncPoint, p_ret); + } else { + f_clientSyncAndVerdict(p_syncPoint, p_ret); + } + } + + /** + * @desc Calls either self synchronization function if + * invoking component is the MTC, otherwise + * calls client synchronization. After that it + * sets a preamble specific verdict based on the + * specified return code. + * This function allows to implement TTCN-3 functions + * in a way that they can be used in both non-concurrent + * as well as concurrent test cases. + * @remark This function should _not_ be called if the MTC + * acts as a client (and not a server) in a concurrent + * test case. In this case f_clientSyncAndVerdictPreamble + * should be used instead. + * @param p_syncPoint Synchronization point name/id + * @param p_ret Current behavior execution status + * @see LibCommon_Sync.f_clientSyncAndVerdict + * @see LibCommon_VerdictControl.f_setVerdictPreamble + */ + function f_selfOrClientSyncAndVerdictPreamble( in charstring p_syncPoint, + in FncRetCode p_ret) + runs on SelfSyncComp { + if ( self == mtc ) { + // then assume we are running non-conurrent test case + f_selfSyncAndVerdictPreamble(p_syncPoint, p_ret); + } else { + f_clientSyncAndVerdictPreamble(p_syncPoint, p_ret); + } + } + + /** + * @desc Calls either self synchronization function if + * invoking component is the MTC, otherwise + * calls client synchronization. After that it + * sets a preamble specific verdict based on the + * specified return code. + * This function allows to implement TTCN-3 functions + * in a way that they can be used in both non-concurrent + * as well as concurrent test cases. + * @remark This function should _not_ be called if the MTC + * acts as a client (and not a server) in a concurrent + * test case. In this case f_clientSyncAndVerdictTestBody + * should be used instead. + * @param p_syncPoint Synchronization point name/id + * @param p_ret Current behavior execution status + * @see LibCommon_Sync.f_clientSyncAndVerdict + * @see LibCommon_VerdictControl.f_setVerdictPreamble + */ + function f_selfOrClientSyncAndVerdictTestBody( in charstring p_syncPoint, + in FncRetCode p_ret) + runs on SelfSyncComp { + if ( self == mtc ) { + // then assume we are running non-conurrent test case + f_selfSyncAndVerdictTestBody(p_syncPoint, p_ret); + } else { + f_clientSyncAndVerdictTestBody(p_syncPoint, p_ret); + } + } + + /** + * @desc Function kept for backward compatibility + * @see f_selfOrClientSyncAndVerdictPreamble + * + */ + function f_selfOrClientSyncAndVerdictPR( in charstring p_syncPoint, + in FncRetCode p_ret) + runs on SelfSyncComp { + f_selfOrClientSyncAndVerdictPreamble(p_syncPoint, p_ret); + } + + } // end group syncFunctions + + group syncCompStateHandling { + + /** + * + * @desc This function updates the state (stack) of a + * sync client or self sync component. This stack is + * key in the shutdown handling of test components. + * It adds the new state name to the top of the + * sync component stack of states. + * The state will only be added in case of a current + * execution status of e_success. + * @param p_newSyncCompState Name of state which was attempted to be reached. + * @param p_ret Current behavior execution status + * @remark If the state of component changes this function must be + * _at least_ called from your test suite prior to f_selfSync + * or f_clientSync which is the only definite place for the + * shutdown default invocation! + * @see LibCommon_Sync.a_dummyShutDown + * @see LibCommon_Sync.f_selfSync + * @see LibCommon_Sync.f_clientSync + */ + function f_addSyncCompState(in charstring p_newSyncCompState, + in FncRetCode p_ret) + runs on ClientSyncComp { + if ( p_ret == e_success ) { + if ( f_isItemOnStringStack(v_stateStack,p_newSyncCompState) ) { + log("**** f_addSyncCompState: WARNING: Attempt to add state which is already on sync state stack! No additition done.****"); + } else { + f_pushStringStack(v_stateStack,p_newSyncCompState); + } + } + } // end function f_addSyncCompState + + /** + * + * @desc This function returns the top state on the sync + * state stack of a sync client or self sync + * component and removes it from the stack + * This function cna be used, e.g., in a while + * statement within a postamble or shutdown + * implementation + * @param p_state State on top of the state stack. + * @return false if state stack is empty, true otherwise + * @see LibCommon_Sync.a_dummyShutDown + */ + function f_getTopSyncCompState( out charstring p_state ) + runs on ClientSyncComp + return boolean { + if ( not f_peekStringStackTop(v_stateStack,p_state) ) { + p_state := "IDLE"; + return false; + } + f_popStringStack(v_stateStack); + return true; + } // end function f_getTopSyncCompState + + /* + * @desc This function removes the last state on the state stack + * of a sync client or self sync component. + * This stack is key in the shutdown handling of test + * components. + * @see LibCommon_Sync.a_dummyShutDown + */ + function f_popSyncCompState() + runs on ClientSyncComp { + f_popStringStack(v_stateStack); + } // end function f_popSyncCompState + + /** + * + * @desc This function returns the top state on the sync state + * stack of a sync client or self sync component. It + * does not remove it from the stack + * This stack is key in the shutdown handling of test + * components. + * @param p_state State on top of the state stack. + * @return false if state stack is empty, true otherwise + * @see LibCommon_Sync.a_dummyShutDown + */ + function f_peekTopSyncCompState(out charstring p_state) + runs on ClientSyncComp + return boolean { + return f_peekStringStackTop(v_stateStack,p_state); + } // end function f_peekTopSyncCompState + + /** + * @desc This function checks if the sync state stack + * of a sync client or self sync component is empty. + * This stack is key in the shutdown handling of test + * components. + * @see LibCommon_Sync.a_dummyShutDown + */ + function f_isSyncCompStateStackEmpty() + runs on ClientSyncComp + return boolean { + return f_isStringStackEmpty(v_stateStack); + } // end function f_isSyncCompStateStackEmpty + + } // end group syncCompStateHandling + + group shutDownAltsteps { + + /** + * @desc This is an example of a shutdown altstep which can be + * used as a "template" for a interface specific shutdown + * altstep or possily as a first temporary solution in + * test case development.

+ * This altstep shall be activated as a default as the + * first statement in each test case function which drives + * an interface, i.e., in MTC behavior of single component + * and in each client behavior of multi component test + * cases.
+ * The required behavior from this altstep is to:

+ * 1) expect the STOP either via the test component + * syncPort

+ * 2) upon its arrival it should shut down the SUT + * gracefully based on the current component state

+ * The current component state should have been + * previously kept uptodate from a test suite via the + * f_addSyncCompState function. This default will then be + * (automatically) invoked either from within f_selfSync + * or f_clientSync.
+ * Note that shutdown defaults can be written as + * _interface specific_ - they do not need to be test case + * or test component specific! See another example of a + * shutdown altstep in the sync module. + * @see LibCommon_Sync.f_addSyncCompState + * @see LibCommon_Sync.f_selfSync + * @see LibCommon_Sync.f_clientSync + * @see LibCommon_SyncExamples.a_exampleShutDown + * @remark Your application specific shutdown altstep + * implementation(s) should _not_ be defined in this + * module but as part of your test suite or application specific + * modules. + */ + altstep a_dummyShutDown() + runs on SelfSyncComp { + [] syncPort.receive(m_syncServerStop){ + var charstring v_state := ""; + tc_sync.stop; + log("**** a_dummyShutDown: Test component received STOP signal from sync server - going to IDLE state ****"); + while ( f_getTopSyncCompState(v_state) ) { + if ( v_state == "x" ) { + // then do something + } else if ( v_state == "y" ) { + // then do something else + } + } // end while + f_disconnect4SelfOrClientSync(); + // unmap/disconnect more if needed + log("**** a_dummyShutDown: -> Test component stopping itself now! ****") ; + stop ; + } + } // end altstep a_dummyShutDown + + /** + * @desc Shutdown alstep in case the sync server is requesting shutdown. + * + * @remark User shall stop the component + */ + altstep a_shutdown() + runs on ClientSyncComp { + [] syncPort.receive(m_syncServerStop){ + tc_sync.stop ; + log("**** a_shutdown: Test component received STOP signal from MTC **** "); + } + } + + } // end group shutDownAltsteps + + } // end group basicUserRelevantDefinitions + + group advancedUserRelevantDefinitions { + + group serverRelated { + + /** + * @desc Implements synchronization of "n" clients from server + * side. If a problem occurs, then server sends STOP to + * all clients. Waits for PX_TSYNC_TIME_LIMIT to let + * clients finish executing their behavior until this + * synchronization point. See f_serverSyncClientsTimed for + * overwriting this later timing constraint! + * This function sets the server component verdict. + * @remark The use of this function requires prior connection of + * the server sync port! + * @see LibCommon_Sync.f_connect4SelfOrClientSync + * @see LibCommon_Sync.PX_TSYNC_TIME_LIMIT + * @see LibCommon_Sync.f_serverSyncClientsTimed + * @param p_noOfClients number of clients to be synchronized + * @param p_syncId synchronization point name/id + */ + function f_serverSyncClients( in UInt p_noOfClients, in charstring p_syncId ) + runs on ServerSyncComp { + f_serverSyncClientsTimed(p_noOfClients,p_syncId, PX_TSYNC_TIME_LIMIT); + } + + /** + * @desc Implements synchronization of "n" clients from server + * side including intermediate synchronization. + * If a problem occurs, then server sends STOP to + * all clients. Waits for PX_TSYNC_TIME_LIMIT to let + * clients finish executing their behavior until this + * synchronization point. See f_serverSyncClientsTimed for + * overwriting this later timing constraint! + * This function sets the server component verdict. + * @remark The use of this function requires prior connection of + * the server sync port! + * @see LibCommon_Sync.f_connect4SelfOrClientSync + * @see LibCommon_Sync.PX_TSYNC_TIME_LIMIT + * @see LibCommon_Sync.f_serverSyncClientsTimed + * @param p_noOfClients number of clients to be synchronized + * @param p_syncId synchronization point name/id + */ + function f_serverSyncClientsIntermediateSync( in UInt p_noOfClients, in charstring p_syncId, in UInt p_NoOfClientIntermediate, in template (present) charstring p_syncIdIntermediate ) + runs on ServerSyncComp { + f_serverSyncClientsTimedIntermediateSync(p_noOfClients,p_syncId, p_NoOfClientIntermediate, p_syncIdIntermediate, PX_TSYNC_TIME_LIMIT); + } + + /** + * @desc Handles synchronization of clients from server side. + * If problem occurs, then server sends STOP to all clients. + * This function sets the server verdict. + * @remark The use of this function requires prior connection of + * the server sync ports! + * @param p_NoOfClients number of clients to be synchronized + * @param p_syncId synchronization point name/id + * @param p_execTimeLimit time limit given to all clients to finish the execution + * of their behavior up to this synchronization point + * @see LibCommon_Sync.f_connect4SelfOrClientSync + */ + function f_serverSyncClientsTimed(in UInt p_NoOfClients, + in charstring p_syncId, + float p_execTimeLimit ) + runs on ServerSyncComp { + f_serverSyncClientsTimedIntermediateSync(p_NoOfClients, p_syncId, 0, ?, p_execTimeLimit ) + } // end function f_serverSyncClientsTimed + + /** @desc Handles synchronization of clients from server side including + * intermediate synchronization. + * If problem occurs, then server sends STOP to all clients. + * This function sets the server verdict. + * @remark The use of this function requires prior connection of + * the server sync ports! + * @param p_NoOfClients number of clients to be synchronized + * @param p_syncId synchronization point name/id + * @param p_execTimeLimit time limit given to all clients to finish the execution + * of their behavior up to this synchronization point + * @see LibCommon_Sync.f_connect4SelfOrClientSync + * @return execution status + */ + function f_serverSyncClientsTimedIntermediateSync( in UInt p_NoOfClients, + in charstring p_syncId, in UInt p_NoOfClientIntermediate, in template (present) charstring p_syncIdIntermediate, + float p_execTimeLimit ) + runs on ServerSyncComp { + + var integer v_noOfRecvdSyncMsgs := 0, v_noOfRecvdSyncMsgsIntermediate := 0; + var boolean v_stopClients := false; + var ClientSyncCompList v_clientRefs := {}, v_clientRefsIntermediate := {}; + var ClientSyncComp v_clientRef; + + if ( p_syncId == c_prDone ) { + log("**** f_serverSyncClientsTimed: Sync server now starting PREAMBLE synchronization ... ****") ; + } else if ( p_syncId == c_tbDone ) { + log("**** f_serverSyncClientsTimed: Sync server now starting TEST BODY synchronization ... ****") ; + } else if ( p_syncId == c_initDone ) { + log("**** f_serverSyncClientsTimed: Sync server now starting UPPER TESTER synchronization ... ****") ; + } else { + log("**** f_serverSyncClientsTimed: Sync server now starting handling of next synchronization point ... ****") ; + } + tc_sync.start(p_execTimeLimit) ; + alt{ + [v_noOfRecvdSyncMsgsIntermediate != p_NoOfClientIntermediate] syncPort.receive(m_syncClientReady(p_syncIdIntermediate)) -> sender v_clientRef { + if(not f_isPresentInArray(v_clientRef, v_clientRefsIntermediate)) { + v_clientRefsIntermediate[v_noOfRecvdSyncMsgsIntermediate] := v_clientRef; + v_noOfRecvdSyncMsgsIntermediate := v_noOfRecvdSyncMsgsIntermediate + 1; + if (v_noOfRecvdSyncMsgsIntermediate == p_NoOfClientIntermediate) { + f_serverSendToAllClients(v_clientRefsIntermediate, m_syncServerReady(p_syncIdIntermediate)); + } + } + repeat; + } + [] syncPort.receive(m_syncClientReady(p_syncId)) -> sender v_clientRef { + if(not f_isPresentInArray(v_clientRef, v_clientRefs)) { + v_clientRefs[v_noOfRecvdSyncMsgs] := v_clientRef; + v_noOfRecvdSyncMsgs := v_noOfRecvdSyncMsgs + 1; + } + if ( v_noOfRecvdSyncMsgs != p_NoOfClients ) { repeat; } + } + [] syncPort.receive(m_syncClientStop) -> sender v_clientRef { + log("**** f_serverSyncClientsTimed: Sync server received STOP signal from a client - server will wait for all clients to reach their next synchronization point and then stop them! ****") ; + v_stopClients := true; + if(not f_isPresentInArray(v_clientRef, v_clientRefs)) { + v_clientRefs[v_noOfRecvdSyncMsgs] := v_clientRef; + v_noOfRecvdSyncMsgs := v_noOfRecvdSyncMsgs + 1; + } + if ( v_noOfRecvdSyncMsgs != p_NoOfClients ) { repeat; } + + } + [] syncPort.receive(m_syncClientReady(?)) -> sender v_clientRef { + log("**** f_serverSyncClientsTimed: Sync server received client sync message with incorrect synchronization point id which is currently not handled - server will stop all clients! ****") ; + v_stopClients := true; + if(not f_isPresentInArray(v_clientRef, v_clientRefs)) { + v_clientRefs[v_noOfRecvdSyncMsgs] := v_clientRef; + } + } + [] syncPort.receive(SyncCmd :? ) { + log("**** f_serverSyncClientsTimed: Sync server received (invalid) sync message from other sync server - server will stop all clients! ****") ; + v_stopClients := true; } + [] any port.receive { + // leave it to be ok to receive anything else + // in case that the user has added any non-sync ports to + // his/her server component type definition! + } + [] tc_sync.timeout{ + log("**** f_serverSyncClientsTimed: A client is not responding within specified time limit - sync server is sending stop to all clients! ****"); + v_stopClients := true; } + } //end alt + if (v_noOfRecvdSyncMsgsIntermediate != p_NoOfClientIntermediate) { + v_stopClients := true; + } + tc_sync.stop ; + if ( v_stopClients ) { + setverdict(inconc); + // then send out STOP sync msg + f_serverSendToAllClients(v_clientRefs, m_syncServerStop); + f_serverWaitForAllClientsToShutDown(); // function will never return! + } else { + setverdict(pass); + // then send out READY sync msg + f_serverSendToAllClients(v_clientRefs, m_syncServerReady(p_syncId)); + if ( p_syncId == c_prDone ) { + log("**** f_serverSyncClientsTimed: Sync server successfully passed PREAMBLE synchronization point. ****") ; + } else if ( p_syncId == c_tbDone ) { + log("**** f_serverSyncClientsTimed: Sync server successfully passed TEST BODY synchronization point. ****") ; + } else { + log("**** f_serverSyncClientsTimed: Sync server successfully passed synchronization point. ****") ; + } + } + } // end function f_serverSyncClientsTimedIntermediateSync + + /** + * @desc This function is intended only for use on the sync + * server component in concurrent TTCN-3 test cases. + * It waits for all components to finish execution within + * the PX_TSYNC_TIME_LIMIT. If a timeout occurs + * the server will stop all clients. + * This function sets the server component verdict. + */ + function f_serverWaitForAllClientsToStop() + runs on ServerSyncComp { + tc_sync.start; + alt { + [] all component.done { + tc_sync.stop; + log("**** f_serverWaitForAllClientsToStop: All sync clients have finished their execution. Sync server now terminating test case. ****") ; + } + [] tc_sync.timeout { + log("**** f_serverWaitForAllClientsToStop: Not all sync clients have finshed execution within the sync time limit. Sync server will stop test case! ****") ; + stop; + } + } // end alt + setverdict(pass); + } // end function f_serverWaitForAllClientsToStop + + } // end group serverRelated + + group clientRelated { + + /** + * @desc This function creates the connection needed to + * execute client synchronization functions + * @see LibCommon_Sync.f_clientSync + * @see LibCommon_Sync.f_clientSendStop + */ + function f_connect4ClientSync() + runs on ClientSyncComp { + connect(self:syncPort, mtc:syncPort); + }// end function f_connect4ClientSync + + /** + * @desc This function removes the connection needed + * to execute client synchronization functions + * @see LibCommon_Sync.f_clientSync + * @see LibCommon_Sync.f_clientSendStop + */ + function f_disconnect4ClientSync() + runs on ClientSyncComp { + disconnect(self:syncPort, mtc:syncPort); + }// end function f_disconnect4ClientSync + + /** + * @desc This function combines client verdict setting with its + * synchronization for use,e.g, after or within a + * test body implementation. + * Note that such premables can _not_ be reused in non- + * concurrent test cases. This can be achieved by using + * the f_selfOrClientSyncAndVerdict function instead. + * This function sets the client component verdict. + * @param p_syncId Synchronization point name/id + * @param p_ret Current behavior execution status + * @remark The use of this function requires prior connection + * of the client sync port! + * @see LibCommon_Sync.f_connect4ClientSync + * @see LibCommon_Sync.f_connect4SelfOrClientSync + * @see LibCommon_VerdictControl.f_setVerdict + * @see LibCommon_Sync.f_selfOrClientSyncAndVerdict + */ + function f_clientSyncAndVerdict(in charstring p_syncId, + in FncRetCode p_ret) + runs on ClientSyncComp { + if(vc_testcaseStep == e_preamble) { + f_clientSyncAndVerdictPreamble(p_syncId, p_ret); + } else if(vc_testcaseStep == e_testBody) { + f_clientSyncAndVerdictTestBody(p_syncId, p_ret); + } + else { + f_clientSyncAndVerdictPostamble(p_syncId, p_ret); + } + } + + /** + * @desc This function combines client verdict setting with its + * synchronization for use after or within a preamble + * implementation. + * Note that such preambles can _not_ be reused in non- + * concurrent test cases. + * This function sets the client component verdict. + * @remark The use of this function requires prior connection + * of the client sync port! + * @see LibCommon_Sync.f_connect4ClientSync + * @see LibCommon_Sync.f_connect4SelfOrClientSync + * @see LibCommon_VerdictControl.f_setVerdictPreamble + * @param p_syncId Synchronization point name/id + * @param p_ret Current behavior execution status + */ + function f_clientSyncAndVerdictPreamble(in charstring p_syncId , + FncRetCode p_ret) + runs on ClientSyncComp { + f_setVerdictPreamble(p_ret); + f_clientSync(p_syncId,p_ret); + vc_testcaseStep := e_testBody; + } + + /** + * @desc This function combines client verdict setting with its + * synchronization for use,e.g, after or within a + * test body implementation. + * Note that such premables can _not_ be reused in non- + * concurrent test cases. This can be achieved by using + * the f_selfOrClientSyncAndVerdict function instead. + * This function sets the client component verdict. + * @param p_syncId Synchronization point name/id + * @param p_ret Current behavior execution status + * @remark The use of this function requires prior connection + * of the client sync port! + * @see LibCommon_Sync.f_connect4ClientSync + * @see LibCommon_Sync.f_connect4SelfOrClientSync + * @see LibCommon_VerdictControl.f_setVerdict + * @see LibCommon_Sync.f_selfOrClientSyncAndVerdict + */ + function f_clientSyncAndVerdictTestBody(in charstring p_syncId, + in FncRetCode p_ret) + runs on ClientSyncComp { + f_setVerdict(p_ret); + f_clientSync(p_syncId,p_ret); + vc_testcaseStep := e_postamble; + } + + /** + * @desc This function combines client verdict setting with its + * synchronization for use after or within a + * postamble implementation. + * Note that such prostambles can _not_ be reused in non- + * concurrent test cases. + * This function sets the client component verdict. + * @remark The use of this function requires prior connection + * of the client sync port! + * @see LibCommon_Sync.f_connect4ClientSync + * @see LibCommon_Sync.f_connect4SelfOrClientSync + * @see LibCommon_VerdictControl.f_setVerdictPostamble + * @param p_syncId Synchronization point name/id + * @param p_ret Current behavior execution status + */ + function f_clientSyncAndVerdictPostamble(in charstring p_syncId , + in FncRetCode p_ret) + runs on ClientSyncComp { + f_setVerdictPostamble(p_ret); + f_clientSync(p_syncId,p_ret); + } + + /** + * @desc This function handles synchronization of a sync client + * with the server. In case of successful execution it sends + * a READY message to the server and waits the READY back. + * The time used for waiting is defined by PX_TSYNC_TIME_LIMIT. + * In case of a non successful execution status it + * sends a STOP message to the server. + * In both cases the receipt of a STOP message or no + * response from the server it will trigger the shutdown + * default (if activated). + * This function will set only the client verdict to INCONC + * (and stop its execution) if no STOP response is received + * from the server within the PX_TSYNC_TIME_LIMIT + * or if no shutdown default is activated. In all other + * cases the client verdict is NOT set. + * @param p_syncId Synchronization point name/id + * @param p_ret Current behavior execution status + * @remark The use of this function requires prior connection + * of the client sync port! + * @see LibCommon_Sync.f_connect4ClientSync + * @see LibCommon_Sync.f_connect4SelfOrClientSync + * @see LibCommon_Sync.PX_TSYNC_TIME_LIMIT + * @see LibCommon_Sync.a_dummyShutDown + * @see LibCommon_Sync.f_clientSendStop + * @return Updated execution status + */ + function f_clientSync( in charstring p_syncId , + in FncRetCode p_ret ) + runs on ClientSyncComp + return FncRetCode{ + + if (p_ret == e_success){ + syncPort.send(m_syncClientReady(p_syncId)); + tc_sync.start; + alt{ + [] syncPort.receive(m_syncServerReady(p_syncId)){ + tc_sync.stop ; } + [] tc_sync.timeout{ + log("**** f_clientSync: Sync client did not receive message from sync server within the specified time limit - sync client will ask sync server to stop test case! ****") ; + f_clientSendStop(); } // function will not return! + } //end alt + } //end if + else { + log("**** f_clientSync: Execution status indicates that execution of test component behavior was not successful - sync client will ask sync server to stop test case! ****") ; + f_clientSendStop(); // function will not return! + } + if ( p_syncId == c_prDone ) { + log("**** f_clientSync: Sync client successfully passed PREAMBLE synchronization point. ****") ; + } else if ( p_syncId == c_tbDone ) { + log("**** f_clientSync: Sync client successfully passed TEST BODY synchronization point. ****") ; + } else { + log("**** f_clientSync: Sync client successfully passed synchronization point. ****") ; + } + return e_success ; + + } // end function f_clientSync + + /** + * @desc This function can be used to request the shutdown a + * multi component test case _prior_ to reaching a + * synchronization point. It sends a STOP message to + * the sync server and awaits then the STOP from the server + * which will trigger the shutdown default (if activated). + * This function will set the server verdict to INCONC (and + * stop the test case) if no shutdown default is activated. + * This function will set only the client verdict to INCONC + * (and stop its execution) if no STOP response is received + * from the server within the PX_TSYNC_TIME_LIMIT + * or if no shutdown default is activated. In all other + * cases the client verdict is NOT set. + * @remark The use of this function requires prior connection + * of the client sync port! + * @see LibCommon_Sync.f_connect4ClientSync + * @see LibCommon_Sync.f_connect4SelfOrClientSync + * @see LibCommon_Sync.PX_TSYNC_TIME_LIMIT + * @see LibCommon_Sync.a_dummyShutDown + */ + function f_clientSendStop() + runs on ClientSyncComp { + log("**** f_clientSendStop: Sync client requesting from server to stop test case (including itself). ****") ; + syncPort.send(m_syncClientStop) ; + tc_sync.start; + alt{ + [] tc_sync.timeout{ + log("**** f_clientSendStop: Stopping sync client without shutdown - either no shutdown default active or no stop received from server. ****") ; + setverdict(inconc); + stop ; + } + }//end alt + tc_sync.stop; + stop; // stop here if shutdown default does not stop + } + + } // end group clientRelated + + } // end group advancedUserRelevantDefinitions + + group otherSyncModuleDefinitions { + + group syncModuleparams { + /** + * + * @desc Default time limit for a sync client to reach a + * synchronization point + */ + modulepar float PX_TSYNC_TIME_LIMIT := 120.0; + + /* + * @desc Default time limit for a sync client to finish + * its execution of the shutdown default + */ + modulepar float PX_TSHUT_DOWN_TIME_LIMIT := 120.0; + } + + group otherSyncTypes { + + type record of charstring SyncPointList; + + type record of ClientSyncComp ClientSyncCompList; + + } // end group otherSyncTypes + + group otherSelfSyncRelatedDefinitions { + + /** + * @desc This function creates the connection needed to + * execute self sync functions + * @see LibCommon_Sync.f_selfSync + * @see LibCommon_Sync.f_selfSyncStop + */ + function f_connect4SelfSync() + runs on SelfSyncComp { + connect(self:syncSendPort, self:syncPort); + }// end function f_connect4SelfSync + + /** + * @desc This function removes the connection needed + * to execute self sync functions + * @see LibCommon_Sync.f_selfSync + * @see LibCommon_Sync.f_selfSyncStop + */ + function f_disconnect4SelfSync() + runs on SelfSyncComp { + disconnect(self:syncSendPort, self:syncPort); + }// end function f_disconnect4SelfSync + + /** + * @desc This function combines MTC verdict setting with self + * synchronization for use in the preamble / test body / postamble + * @param p_syncId Synchronization point name/id + * @param p_ret Current behavior execution status + * @see LibCommon_VerdictControl.f_setVerdict + * @see LibCommon_Sync.f_selfSync + * @see LibCommon_Sync.a_dummyShutDown + */ + function f_selfSyncAndVerdict( in charstring p_syncId, + in FncRetCode p_ret ) + runs on SelfSyncComp { + if(vc_testcaseStep == e_preamble) { + f_selfSyncAndVerdictPreamble(p_syncId, p_ret); + } else if(vc_testcaseStep == e_testBody) { + f_selfSyncAndVerdictTestBody(p_syncId, p_ret); + } + else { + f_selfSyncAndVerdictPostamble(p_syncId, p_ret); + } + } + + /** + * @desc This function combines MTC verdict setting with self + * synchronization for use after the preamble. + * @param p_syncId Synchronization point name/id + * @param p_ret Current behavior execution status + * @see LibCommon_VerdictControl.f_setVerdictPreamble + * @see LibCommon_Sync.f_selfSync + */ + function f_selfSyncAndVerdictPreamble( in charstring p_syncId, + in FncRetCode p_ret ) + runs on SelfSyncComp { + f_setVerdictPreOrPostamble(p_ret); + f_selfSync(p_syncId,p_ret); + vc_testcaseStep := e_testBody; + } + + /** + * @desc This function combines MTC verdict setting with self + * synchronization for use after the test body. + * @param p_syncId Synchronization point name/id + * @param p_ret Current behavior execution status + * @see LibCommon_VerdictControl.f_setVerdict + * @see LibCommon_Sync.f_selfSync + */ + function f_selfSyncAndVerdictTestBody( in charstring p_syncId, + in FncRetCode p_ret ) + runs on SelfSyncComp { + f_setVerdict(p_ret); + f_selfSync(p_syncId,p_ret); + vc_testcaseStep := e_postamble; + } + + /** + * @desc This function combines MTC verdict setting with self + * synchronization for use after the postamble. + * @param p_syncId Synchronization point name/id + * @param p_ret Current behavior execution status + * @see LibCommon_VerdictControl.f_setVerdictPostamble + * @see LibCommon_Sync.f_selfSync + */ + function f_selfSyncAndVerdictPostamble( in charstring p_syncId , + in FncRetCode p_ret ) + runs on SelfSyncComp { + f_setVerdictPreOrPostamble(p_ret); + f_selfSync(p_syncId,p_ret); + } + + /** + * @desc This function synchronizes a MTC with itself. In case + * of a non successful execution status it sends a STOP + * message to itself and invokes that way the + * shutdown default (if activated). + * This function will set the server verdict to INCONC (and + * stop the test case) if no shutdown default is activated. + * Otherwise no verdict is set. + * @remark Sync ports should be connected prior to the invocation + * of this function! + * @param p_syncId Synchronization point name/id + * @param p_ret Current behavior execution status + * @return Updated execution status + * @see LibCommon_Sync.f_connect4SelfSync + * @see LibCommon_Sync.a_dummyShutDown + */ + function f_selfSync( in charstring p_syncId , + in FncRetCode p_ret ) + runs on SelfSyncComp + return FncRetCode{ + if (p_ret != e_success){ + f_selfSyncStop() ; // function will not return! + } + if ( p_syncId == c_prDone ) { + log("**** f_selfSync: Successfully passed PREAMBLE synchronization point. ****") ; + } else if ( p_syncId == c_tbDone ) { + log("**** f_selfSync: Successfully passed TEST BODY synchronization point. ****") ; + } else { + log("**** f_selfSync: Successfully passed synchronization point. ****") ; + } + return e_success ; + }// end function f_selfSync + + /** + * @desc This function can be used to shut down a test case _prior_ + * to reaching a synchronization point. it sends a STOP + * message to itself and invokes that way the + * shutdown default (if activated). + * This function will set the server verdict to INCONC (and + * stop the test case) if no shutdown default is activated. + * Otherwise no verdict is set. + * @remark Sync ports should be connected prior to the invocation + * of this function! + * @see LibCommon_Sync.f_connect4SelfSync + */ + function f_selfSyncStop() + runs on SelfSyncComp { + + log("**** f_selfSyncStop: MTC requests to stop test case (itself). ****") ; + syncSendPort.send(m_syncServerStop) ; // this MUST be _server_ for the default to catch! + tc_sync.start(PX_TSYNC_TIME_LIMIT); + alt{ + [] tc_sync.timeout{ + log("**** f_selfSyncStop: Stopping MTC without shutdown - either no shutdown default active or missing syncPort connection ****") ; + setverdict(inconc); + stop ; + } + }//end alt + tc_sync.stop; + stop; // if shutdown default is not activated or if it does not stop + } // end function f_selfSyncStop + + } // end group otherSelfSyncRelatedDefinitions + + /** + * + * @desc The sychronization protocol is conceptually based on + * named synchronization. Each synchronization point + * has it own specific synchronization message. This + * makes each synchronization unique, and allows, e.g., to + * ensure that a server synchronizes only clients which have + * reached the same synchronization point. + */ + group syncProtocolDefinition { + + type union SyncCmd { + ClientReady clientReady, + ServerReady serverReady, + ClientStop clientStop, + ServerStop serverStop + } + + type record ClientReady { + charstring syncPointId + } + + type record ServerReady { + charstring syncPointId + } + + type record ClientStop {} + + type record ServerStop {} + + } // end group syncProtocolDefinition + + group syncMessages { + template SyncCmd m_syncClientReady( template (present) charstring p_syncId ) := { + clientReady := { p_syncId } + } + + template SyncCmd m_syncServerReady( template (present) charstring p_syncId ) := { + serverReady := { p_syncId } + } + + template SyncCmd m_syncClientStop := { + clientStop := {} + } + + template SyncCmd m_syncServerStop := { + serverStop := {} + } + + } // end group syncMessages + + group otherSyncFunctions { + + /** + * @desc Makes server send a sync message to all known clients + * @param p_clientRefs List of client references to which the message is to be send + * @param p_syncCmd The actual synchronization message to be sent out + */ + function f_serverSendToAllClients( in ClientSyncCompList p_clientRefs, + in template (value) SyncCmd p_syncCmd) + runs on ServerSyncComp { + var integer i:=0; + for (i:=0; i< sizeof(p_clientRefs); i:=i+1 ){ + syncPort.send(p_syncCmd) to valueof(p_clientRefs[i]); + } + } // end function f_serverSendToAllClients + + /** + * @desc This function is intended only for use on server in concurrent + * TTCN-3 test cases. It waits for all components to shut down + * within the PX_TSHUT_DOWN_TIME_LIMIT. If a timeout occurs + * it aborts the test case (no matter how far clients got with their + * shutdown). + * This function sets the server verdict. + */ + function f_serverWaitForAllClientsToShutDown() + runs on ServerSyncComp { + + tc_shutDown.start(PX_TSHUT_DOWN_TIME_LIMIT); + alt { + [] syncPort.receive { + // clients may still try to send some sync message + } + [] all component.done { + tc_shutDown.stop; + log("**** f_serverWaitForAllClientsToShutDown: All components have properly shut down. Sync server will now terminate the test case. ****") ; + } + [] tc_shutDown.timeout { + log("**** f_serverWaitForAllClientsToShutDown: Not all clients have properly shutdown within the shut down time limit. Sync server will now terminate test case! ****") ; + } + } // end alt + // cover case that shut down default is NOT activated + setverdict(inconc); + //mtc.stop; + syncPort.send(m_syncServerStop) to self; // this MUST be _server_ for the default to catch! + tc_sync.start(PX_TSYNC_TIME_LIMIT); + alt{ + [] tc_sync.timeout{ + log("**** f_selfSyncStop: Stopping MTC without shutdown - either no shutdown default active or missing syncPort connection ****") ; + setverdict(inconc); + stop ; + } + }//end alt + tc_sync.stop; + stop; // if shutdown default is not activated or if it does not stop + } // end function f_serverWaitForAllClientsToShutDown + + function f_isPresentInArray(in ClientSyncComp p_clientRef, in ClientSyncCompList p_clientRefs) + return boolean { + var integer i; + for(i:=0; i < sizeof(p_clientRefs); i:=i+1) { + if(p_clientRefs[i] == p_clientRef) { + return true; + } + } + return false; + } + } // end group otherSyncFunctions + + } // end group otherSyncDefinitions + +} // end module LibCommon_Sync diff --git a/ttcn/patch_lib_http/LibItsHttp_BinaryMessageBodyTypes.ttcn b/ttcn/patch_lib_http/LibItsHttp_BinaryMessageBodyTypes.ttcn new file mode 100644 index 0000000..0e20b91 --- /dev/null +++ b/ttcn/patch_lib_http/LibItsHttp_BinaryMessageBodyTypes.ttcn @@ -0,0 +1,26 @@ +/** + * @author ETSI / STF545 + * @version $URL$ + * $ID:$ + * @desc This module provides the custom binary types for ITS HTTP based protocols. + * @copyright ETSI Copyright Notification + * No part may be reproduced except as authorized by written permission. + * The copyright and the foregoing restriction extend to reproduction in all media. + * All rights reserved. + */ +module LibItsHttp_BinaryMessageBodyTypes { + + /** + * This file volontary contains a trivial declaration of the type BinaryBodu. + * In accordance with your TTCN-3 module LibItsHttp_XMLTypes, you have to change the BinaryBody typing. + */ + // TODO Add here your custom binary import + + type union BinaryBody { + // TODO Add here your custom variants + octetstring raw + } with { + variant "" + } + +} // End of LibItsHttp_BinaryMessageBodyTypes diff --git a/ttcn/patch_lib_http/LibItsHttp_BinaryTemplates.ttcn b/ttcn/patch_lib_http/LibItsHttp_BinaryTemplates.ttcn new file mode 100644 index 0000000..a217088 --- /dev/null +++ b/ttcn/patch_lib_http/LibItsHttp_BinaryTemplates.ttcn @@ -0,0 +1,32 @@ +/** + * @author ETSI / STF545 + * @version $URL$ + * $ID:$ + * @desc This module provides the custom templates for ITS HTTP based protocols. + * @copyright ETSI Copyright Notification + * No part may be reproduced except as authorized by written permission. + * The copyright and the foregoing restriction extend to reproduction in all media. + * All rights reserved. + */ +module LibItsHttp_BinaryTemplates { + + // LibItsHttp + import from LibItsHttp_BinaryMessageBodyTypes all; + + // TODO Add here your custom binary import + + template (value) BinaryBody m_binary_body_raw( + in template (value) octetstring p_raw + ) := { + raw := p_raw + } // End of template m_binary_body_raw + + template (present) BinaryBody mw_binary_body_raw( + template (present) octetstring p_raw := ? + ) := { + raw := p_raw + } // End of template mw_binary_body_raw + + // TODO Add here your custom binary template + +} // End of module LibItsHttp_BinaryTemplates diff --git a/ttcn/patch_lib_http/LibItsHttp_BinaryTypes.ttcn b/ttcn/patch_lib_http/LibItsHttp_BinaryTypes.ttcn new file mode 100644 index 0000000..da6d0f6 --- /dev/null +++ b/ttcn/patch_lib_http/LibItsHttp_BinaryTypes.ttcn @@ -0,0 +1,9 @@ +module LibItsHttp_BinaryTypes { // FIXME To be removed + + /** + * This file is volontary empry. You have to declare all XSD files required by your project + * In addition, the TTCN-3 module LibItsHttp_XmlMessageBodyTypes have to be updated too. + */ + // TODO Add here your custom binary import + +} // End of module LibItsHttp_BinaryTypes diff --git a/ttcn/patch_lib_http/LibItsHttp_JsonMessageBodyTypes.ttcn b/ttcn/patch_lib_http/LibItsHttp_JsonMessageBodyTypes.ttcn new file mode 100644 index 0000000..32630d2 --- /dev/null +++ b/ttcn/patch_lib_http/LibItsHttp_JsonMessageBodyTypes.ttcn @@ -0,0 +1,23 @@ +module LibItsHttp_JsonMessageBodyTypes { + + // LibMec/LocationAPI + import from LocationAPI_TypesAndValues all; + + /** + * This file volontary contains a trivial declaration of the type JsonBody. + * In accordance with your TTCN-3 module LibItsHttp_XMLTypes, you have to change the JsonBody typing. + */ + // TODO Add here your custom RFCs import + + type union JsonBody { + // TODO Add here your custom variants + UserInfo userInfo, + ZoneInfo zoneInfo, + universal charstring raw + } with { + variant "" + } + +} with { + variant "" +} // End of module LibItsHttp_JsonMessageBodyTypes diff --git a/ttcn/patch_lib_http/LibItsHttp_JsonTemplates.ttcn b/ttcn/patch_lib_http/LibItsHttp_JsonTemplates.ttcn new file mode 100644 index 0000000..f2f7a9b --- /dev/null +++ b/ttcn/patch_lib_http/LibItsHttp_JsonTemplates.ttcn @@ -0,0 +1,59 @@ +/** + * @author ETSI / STF569 + * @version $URL$ + * $ID:$ + * @desc This module provides the custom templates for ITS HTTP based protocols. + * @copyright ETSI Copyright Notification + * No part may be reproduced except as authorized by written permission. + * The copyright and the foregoing restriction extend to reproduction in all media. + * All rights reserved. + */ +module LibItsHttp_JsonTemplates { + + // LibMec/LocationAPI + import from LocationAPI_TypesAndValues all; + import from LocationAPI_Templates all; + + // TODO Add here your custom RFCs import + + // LibItsHttp + import from LibItsHttp_JsonMessageBodyTypes all; + import from LibItsHttp_JSONTypes all; + + template (value) JsonBody m_json_body_raw( + in template (value) charstring p_raw + ) := { + raw := p_raw + } // End of template m_json_body_raw + + template (present) JsonBody mw_json_body_raw( + template (present) charstring p_raw := ? + ) := { + raw := p_raw + } // End of template mw_json_body_raw + + template (value) JsonBody m_body_json_user_info( + in template (value) UserInfo p_user_info + ) := { + userInfo := p_user_info + } // End of template m_body_json_user_info + + template (present) JsonBody mw_body_json_user_info( + template (present) UserInfo p_user_info := ? + ) := { + userInfo := p_user_info + } // End of template mw_body_json_user_info + + template (value) JsonBody m_body_json_zone_info( + in template (value) ZoneInfo p_zone_info + ) := { + zoneInfo := p_zone_info + } // End of template m_body_json_zone_info + + template (present) JsonBody mw_body_json_zone_info( + template (present) ZoneInfo p_zone_info := ? + ) := { + zoneInfo := p_zone_info + } // End of template mw_body_json_zone_info + +} // End of module LibItsHttp_JsonTemplates diff --git a/vagrant/Vagrantfile b/vagrant/Vagrantfile new file mode 100644 index 0000000..86b0359 --- /dev/null +++ b/vagrant/Vagrantfile @@ -0,0 +1,73 @@ +# -*- mode: ruby -*- +# vi: set ft=ruby : + +# All Vagrant configuration is done below. The "2" in Vagrant.configure +# configures the configuration version (we support older styles for +# backwards compatibility). Please don't change it unless you know what +# you're doing. +Vagrant.configure("2") do |config| + # The most common configuration options are documented and commented below. + # For a complete reference, please see the online documentation at + # https://docs.vagrantup.com. + + # Every Vagrant development environment requires a box. You can search for + # boxes at https://atlas.hashicorp.com/search. + config.vm.box = "bento/ubuntu-18.04" + + # Disable automatic box update checking. If you disable this, then + # boxes will only be checked for updates when the user runs + # `vagrant box outdated`. This is not recommended. + # config.vm.box_check_update = false + + config.vm.boot_timeout = 900 + + # Use this hostname to force provisioner script to using SVN instead of external HDD + #config.vm.hostname = "vagrant-prov" + + # Create a forwarded port mapping which allows access to a specific port + # within the machine from a port on the host machine. In the example below, + # accessing "localhost:8080" will access port 80 on the guest machine. + # config.vm.network "forwarded_port", guest: 80, host: 8080 + + # Create a private network, which allows host-only access to the machine + # using a specific IP. + #config.vm.network "private_network", ip: "192.168.4.94" + + # Create a public network, which generally matched to bridged network. + # Bridged networks make the machine appear as another physical device on + # your network. + #config.vm.network "public_network" + + # Share an additional folder to the guest VM. The first argument is + # the path on the host to the actual folder. The second argument is + # the path on the guest to mount the folder. And the optional third + # argument is a set of non-required options. + + # Provider-specific configuration so you can fine-tune various + # backing providers for Vagrant. These expose provider-specific options. + # Example for VirtualBox: + # + config.vm.provider "virtualbox" do |vb| + # Display the VirtualBox GUI when booting the machine + #vb.gui = true + vb.customize ["modifyvm", :id, "--monitorcount", "1"] + vb.customize ["modifyvm", :id, "--vram", "12"] + # Customize the amount of memory on the VM: + vb.memory = "4096" + end + # + # View the documentation for the provider you are using for more + # information on available options. + + # Define a Vagrant Push strategy for pushing to Atlas. Other push strategies + # such as FTP and Heroku are also available. See the documentation at + # https://docs.vagrantup.com/v2/push/atlas.html for more information. + # config.push.define "atlas" do |push| + # push.app = "YOUR_ATLAS_USERNAME/YOUR_APPLICATION_NAME" + # end + + # Enable provisioning with a shell script. Additional provisioners such as + # Puppet, Chef, Ansible, Salt, and Docker are also available. Please see the + # documentation for more information about their specific syntax and use. + config.vm.provision "shell", path: "provisioner.bash", privileged: false +end diff --git a/vagrant/provisioner.bash b/vagrant/provisioner.bash new file mode 100755 index 0000000..03a4b9e --- /dev/null +++ b/vagrant/provisioner.bash @@ -0,0 +1,154 @@ +#!/bin/bash +# Prepare environment for the build +#set -e # Exit with non 0 if any command fails +#set -vx + +# Update system +sudo DEBIAN_FRONTEND=noninteractive apt-get update +sudo DEBIAN_FRONTEND=noninteractive apt-get dist-upgrade -y +sudo DEBIAN_FRONTEND=noninteractive apt-get install software-properties-common -y +sudo DEBIAN_FRONTEND=noninteractive add-apt-repository ppa:linuxuprising/java -y +sudo DEBIAN_FRONTEND=noninteractive apt-get update +sudo DEBIAN_FRONTEND=noninteractive apt-get --allow-unauthenticated install emacs openjdk-11-jre gcc-8 g++-8 git subversion lsof ntp gdb make cmake flex bison autoconf doxygen graphviz libtool libncurses5-dev expect libssl-dev libgcrypt-dev libxml2-dev xutils-dev tcpdump libpcap-dev libwireshark-dev wget tree unzip sshpass kubuntu-desktop valgrind qt5-default qttools5-dev qtmultimedia5-dev libqt5svg5-dev vim tzdata dos2unix xsltproc -y +# Install java +#sudo DEBIAN_FRONTEND=noninteractive apt-get install oracle-java9-installer oracle-java9-set-default -y +#sudo DEBIAN_FRONTEND=noninteractive apt-get --allow-unauthenticated install oracle-java10-installer oracle-java10-set-default -y +#sudo DEBIAN_FRONTEND=noninteractive apt --fix-broken install -y +sudo DEBIAN_FRONTEND=noninteractive apt-get autoremove --purge -y +sudo DEBIAN_FRONTEND=noninteractive apt-get clean + +gcc --version +g++ --version +valgrind --version +java -version + +export HOME=/home/vagrant + +export PATH_DEV=${HOME}/dev +export HOME_FRAMEWORKS=${HOME}/frameworks +export HOME_LIB=${HOME}/lib +export HOME_BIN=${HOME}/bin +export HOME_ETC=${HOME}/etc +export HOME_INC=${HOME}/include +export HOME_TMP=${HOME}/tmp +export HOME_DOCS=${HOME}/docs +export PATH=${HOME_BIN}:${PATH} +export LD_LIBRARY_PATH=${HOME_LIB}:/usr/local/lib:${LD_LIBRARY_PATH} + +OLD_PWD=`pwd` +# Create directories +mkdir -p ${HOME_LIB} +if [ ! -d ${HOME_LIB} ] +then + exit -1 +fi +mkdir -p ${HOME_INC} +if [ ! -d ${HOME_INC} ] +then + exit -1 +fi +mkdir -p ${HOME_BIN} +if [ ! -d ${HOME_BIN} ] +then + exit -1 +fi +mkdir -p ${HOME_TMP} +if [ ! -d ${HOME_TMP} ] +then + exit -1 +fi +mkdir -p ${HOME_DOCS} +if [ ! -d ${HOME_DOCS} ] +then + exit -1 +fi +mkdir -p ${HOME_FRAMEWORKS} +if [ ! -d ${HOME_FRAMEWORKS} ] +then + exit -1 +fi +mkdir -p ${PATH_DEV} +if [ ! -d ${PATH_DEV} ] +then + exit -1 +fi + +cd /home/vagrant/dev +git clone https://forge.etsi.org/gitlab/emergency-communications/NG112 ./STF549_Ng112 +cd /home/vagrant/dev/STF549_Ng112 +cd /home/vagrant/dev/STF549_Ng112/ttcn +git clone https://forge.etsi.org/gitlab/LIBS/LibSip.git ./LibSip +git clone https://forge.etsi.org/gitlab/LIBS/LibIms.git ./LibIms +git clone https://forge.etsi.org/gitlab/LIBS/LibCommon.git ./LibCommon +git clone -bSTF525 https://forge.etsi.org/gitlab/LIBS/LibIts ./LibIt +cd /home/etsi/dev/STF549_Ng112/ttcn/LibIts +rm -fr asn1 t3q xsd +cd ttcn && rm -fr BTP CALM CAM Common DCC DENM GeoNetworking Ipv6OverGeoNetworking IVIM MapemSpatem Pki Security SremSsem V2G +cd /home/vagrant/dev/STF549_Ng112/scripts +chmod 775 *.bash devenv.bash.* +cd /home/vagrant/dev/STF549_Ng112/docker +chmod 775 *.sh +cd /home/vagrant/dev/STF549_Ng112 +chmod 775 ./.jenkins.sh +cd /home/vagrant +ln -sf /home/vagrant/dev/STF549_Ng112/scripts/devenv.bash.ubuntu /home/vagrant/devenv.bash +. /home/vagrant/devenv.bash + +# Install all frameworks + +# Install osip +cd ${HOME_FRAMEWORKS} +git clone git://git.savannah.gnu.org/osip.git ./osip +cd ./osip +./autogen.sh +./configure --prefix=/home/vagrant +make && make install + +# Install GoogleTest +cd ${HOME_FRAMEWORKS} +git clone https://github.com/google/googletest.git googletest +cd ${HOME_FRAMEWORKS}/googletest/ +cmake . +make CXX=g++ +sudo make install + +# Install latest LCOV +cd ${HOME_FRAMEWORKS} +mkdir -p ${HOME_FRAMEWORKS}/lcov +cd ${HOME_FRAMEWORKS}/lcov +wget http://ftp.de.debian.org/debian/pool/main/l/lcov/lcov_1.13.orig.tar.gz +tar xf lcov_1.13.orig.tar.gz +sudo make -C lcov-1.13/ install + +# Install lcov to coveralls conversion +sudo gem install coveralls-lcov + +lcov --version +coveralls-lcov -h + +# Install eclipse +cd ${HOME_FRAMEWORKS} +wget 'http://ftp.halifax.rwth-aachen.de/eclipse/technology/epp/downloads/release/oxygen/2/eclipse-cpp-oxygen-2-linux-gtk-x86_64.tar.gz' -Oeclipse-cpp-oxygen-2-linux-gtk-x86_64.tar.gz +tar -zxvf ./eclipse-cpp-oxygen-2-linux-gtk-x86_64.tar.gz +rm -f ./eclipse-cpp-oxygen-2-linux-gtk-x86_64.tar.gz + +cd /home/vagrant +echo "" >> /home/vagrant/.bashrc +echo "export LD_LIBRARY_PATH=/home/vagrant/dev/etsi_emco/lib:$LD_LIBRARY_PATH" >> /home/vagrant/.bashrc +echo "export PATH=/home/vagrant/bin:$PATH" >> /home/vagrant/.bashrc +echo ". ~/devenv.bash" >> /home/vagrant/.bashrc + +. /home/vagrant/.bashrc +cd /home/vagrant/dev/STF549_Ng112/scripts +./build_titan.bash +. /home/vagrant/devenv.bash +./update_its_project.bash +cd /home/vagrant/dev/etsi_emco/src/TestCodec/objs +../bin/testcodec_generate_makefile.bash +../bin/run_all.bash + +cd ${OLD_PWD} + +sudo init 6 + +exit 0 -- GitLab