/********************************************************************\ Name: mscbxx.h Created by: Stefan Ritt Contents: Implementation of MSCB++ API \********************************************************************/ /**** Usage of MSCB++ Example: #include // connect to node 10 at submaster mscb123 midas::mscb m("mscb123", 10); // print node info and all variables std::cout << m << std::endl; // refresh all variables m.read_range(); std::cout << m[5] << std::endl; // index access std::cout << m["In0"] << std::endl; // name access // write value to MSCB m[5] = 1.234; // get index of a variable int i = m.idx("In0"); ****/ #ifndef _MSCBXX_HXX #define _MSCBXX_HXX #include #include #include #include #include #include #include #include #include #include #include "mscb.h" #include "mexcept.h" /*------------------------------------------------------------------*/ /** MIDAS Data Type Definitions min max */ #define TID_BYTE 1 /**< DEPRECATED, use TID_UINT8 instead */ #define TID_UINT8 1 /**< unsigned byte 0 255 */ #define TID_SBYTE 2 /**< DEPRECATED, use TID_INT8 instead */ #define TID_INT8 2 /**< signed byte -128 127 */ #define TID_CHAR 3 /**< single character 0 255 */ #define TID_WORD 4 /**< DEPRECATED, use TID_UINT16 instead */ #define TID_UINT16 4 /**< two bytes 0 65535 */ #define TID_SHORT 5 /**< DEPRECATED, use TID_INT16 instead */ #define TID_INT16 5 /**< signed word -32768 32767 */ #define TID_DWORD 6 /**< DEPRECATED, use TID_UINT32 instead */ #define TID_UINT32 6 /**< four bytes 0 2^32-1 */ #define TID_INT 7 /**< DEPRECATED, use TID_INT32 instead */ #define TID_INT32 7 /**< signed dword -2^31 2^31-1 */ #define TID_BOOL 8 /**< four bytes bool 0 1 */ #define TID_FLOAT 9 /**< 4 Byte float format */ #define TID_DOUBLE 10 /**< 8 Byte float format */ #define TID_STRING 12 /**< zero terminated string */ std::map prefixMap = { {PRFX_PICO, "pico"}, {PRFX_NANO, "nano"}, {PRFX_MICRO, "micro"}, {PRFX_MILLI, "milli"}, {PRFX_NONE, ""}, {PRFX_KILO, "kilo"}, {PRFX_MEGA, "mega"}, {PRFX_GIGA, "giga"}, {PRFX_TERA, "tera"} }; std::map unitTable = { {UNIT_METER, "meter"}, {UNIT_GRAM, "gram"}, {UNIT_SECOND, "second"}, {UNIT_MINUTE, "minute"}, {UNIT_HOUR, "hour"}, {UNIT_AMPERE, "ampere"}, {UNIT_KELVIN, "kelvin"}, {UNIT_CELSIUS, "deg. celsius"}, {UNIT_FARENHEIT, "deg. farenheit"}, {UNIT_HERTZ, "hertz"}, {UNIT_PASCAL, "pascal"}, {UNIT_BAR, "bar"}, {UNIT_WATT, "watt"}, {UNIT_VOLT, "volt"}, {UNIT_OHM, "ohm"}, {UNIT_TESLA, "tesls"}, {UNIT_LITERPERSEC, "liter/sec"}, {UNIT_RPM, "RPM"}, {UNIT_FARAD, "farad"}, {UNIT_BOOLEAN, "boolean"}, {UNIT_BYTE, "byte"}, {UNIT_WORD, "word"}, {UNIT_DWORD, "dword"}, {UNIT_ASCII, "ascii"}, {UNIT_STRING, "string"}, {UNIT_BAUD, "baud"}, {UNIT_PERCENT, "percent"}, {UNIT_PPM, "RPM"}, {UNIT_COUNT, "counts"}, {UNIT_FACTOR, "factor"} }; /*------------------------------------------------------------------*/ namespace midas { class mscb; class u_mscb { private: // union to hold data union { uint8_t m_uint8; int8_t m_int8; uint16_t m_uint16; int16_t m_int16; uint32_t m_uint32; int32_t m_int32; uint64_t m_uint64; int64_t m_int64; bool m_bool; float m_float; double m_double; std::string *m_string; uint8_t m_data[8]; }; int m_tid; int m_size; int m_fd; int m_node_address; int m_var_index; public: u_mscb(int fd, int adr, int index, int tid, int size) : m_string{}, m_tid{tid}, m_size{size}, m_fd{fd}, m_node_address{adr}, m_var_index{index} {}; // Destructor ~u_mscb() {}; // getters & setters int get_tid() { return m_tid; } // get value as certain type template T get() { if (m_tid == TID_UINT8) return (T) m_uint8; else if (m_tid == TID_INT8) return (T) m_int8; else if (m_tid == TID_UINT16) return (T) m_uint16; else if (m_tid == TID_INT16) return (T) m_int16; else if (m_tid == TID_UINT32) return (T) m_uint32; else if (m_tid == TID_INT32) return (T) m_int32; else if (m_tid == TID_BOOL) return (T) m_bool; else if (m_tid == TID_FLOAT) return (T) m_float; else if (m_tid == TID_DOUBLE) return (T) m_double; else mthrow("Invalid type ID " + std::to_string(m_tid)); } // get value as string void get(std::string &s) { if (m_tid == TID_UINT8) s = std::to_string(m_uint8); else if (m_tid == TID_INT8) s = std::to_string(m_int8); else if (m_tid == TID_UINT16) s = std::to_string(m_uint16); else if (m_tid == TID_INT16) s = std::to_string(m_int16); else if (m_tid == TID_UINT32) s = std::to_string(m_uint32); else if (m_tid == TID_INT32) s = std::to_string(m_int32); else if (m_tid == TID_BOOL) s = std::string(m_bool ? "true" : "false"); else if (m_tid == TID_FLOAT) s = std::to_string(m_float); else if (m_tid == TID_DOUBLE) s = std::to_string(m_double); else if (m_tid == TID_STRING) s = *m_string; else mthrow("Invalid type ID " + std::to_string(m_tid)); } void set_value(std::string v) { *m_string = v; } void set_data(const unsigned char *pdata) { std::memset(m_data, 0, sizeof(m_data)); std::memcpy(m_data, pdata, m_size); } // write value to MSCB node int write(void) { if (m_tid == TID_STRING) mscb_write(m_fd, m_node_address, m_var_index, (void *)m_string->c_str(), m_size); else mscb_write(m_fd, m_node_address, m_var_index, &m_uint8, m_size); return 1; } // overload conversion operator template operator T() { if (m_tid == TID_UINT8) return (T) m_uint8; else if (m_tid == TID_INT8) return (T) m_int8; else if (m_tid == TID_UINT16) return (T) m_uint16; else if (m_tid == TID_INT16) return (T) m_int16; else if (m_tid == TID_UINT32) return (T) m_uint32; else if (m_tid == TID_INT32) return (T) m_int32; else if (m_tid == TID_BOOL) return (T) m_bool; else if (m_tid == TID_FLOAT) return (T) m_float; else if (m_tid == TID_DOUBLE) return (T) m_double; else mthrow("Invalid type ID " + std::to_string(m_tid)); } // string conversion operator operator std::string() { std::string s; get(s); return s; } // overload stream out operator friend std::ostream &operator<<(std::ostream &output, u_mscb &m) { std::string s = m; output << s; return output; }; // Overload the Assignment Operators uint8_t operator=(uint8_t v) { if (m_tid != TID_UINT8) mthrow("Invalid data type \"uint8_t\" for variable of type " + std::to_string(m_tid)); m_uint8 = v; write(); return v; } int8_t operator=(int8_t v) { if (m_tid != TID_INT8) mthrow("Invalid data type \"int8_t\" for variable of type " + std::to_string(m_tid)); m_int8 = v; write(); return v; } uint16_t operator=(uint16_t v) { if (m_tid != TID_UINT16) mthrow("Invalid data type \"uint16_t\" for variable of type " + std::to_string(m_tid)); m_uint16 = v; write(); return v; } int16_t operator=(int16_t v) { if (m_tid != TID_INT16) mthrow("Invalid data type \"int16_t\" for variable of type " + std::to_string(m_tid)); m_int16 = v; write(); return v; } uint32_t operator=(uint32_t v) { if (m_tid != TID_UINT32) mthrow("Invalid data type \"uint32_t\" for variable of type " + std::to_string(m_tid)); m_uint32 = v; write(); return v; } int32_t operator=(int32_t v) { if (m_tid == TID_UINT8) m_uint8 = v; else if (m_tid == TID_INT8) m_int8 = v; else if (m_tid == TID_UINT16) m_uint16 = v; else if (m_tid == TID_INT16) m_int16 = v; else if (m_tid == TID_UINT32) m_uint32 = v; else if (m_tid == TID_INT32) m_int32 = v; else mthrow("Invalid data type \"int32_t\" for variable of type " + std::to_string(m_tid)); write(); return v; } bool operator=(bool v) { if (m_tid != TID_BOOL) mthrow("Invalid data type \"bool\" for variable of type " + std::to_string(m_tid)); m_bool = v; write(); return v; } float operator=(float v) { if (m_tid != TID_FLOAT) mthrow("Invalid data type \"float\" for variable of type " + std::to_string(m_tid)); m_float = v; write(); return v; } double operator=(double v) { if (m_tid == TID_FLOAT) m_float = v; else if (m_tid == TID_DOUBLE) m_double = v; else mthrow("Invalid data type \"double\" for variable of type " + std::to_string(m_tid)); write(); return v; } const char *operator=(const char *v) { if (m_tid != TID_STRING) mthrow("Invalid data type \"const char *\" for variable of type " + std::to_string(m_tid)); *m_string = std::string(v); write(); return v; } std::string *operator=(std::string * v) { if (m_tid != TID_STRING) mthrow("Invalid data type \"std::string *\" for variable of type " + std::to_string(m_tid)); *m_string = *v; write(); return v; } std::string operator=(std::string v) { if (m_tid != TID_STRING) mthrow("Invalid data type \"std::string\" for variable of type " + std::to_string(m_tid)); *m_string = v; write(); return v; } }; class mscb { private: std::string m_submaster; std::string m_password; int m_debug; int m_node_address; int m_fd; int m_protocol_version; int m_n_variables; int m_group_address; int m_revision; std::string m_node_name; std::string m_rtc; unsigned int m_uptime; std::vector m_var; std::vector m_data; std::unordered_map m_map; public: mscb(std::string submaster, int address, std::string password = "", int debug = 0) { m_submaster = submaster; m_password = password; m_node_address = address; m_debug = debug; if (m_submaster.empty()) mthrow("Please specify MSCB submaster to mscb() constructor"); char str[80]; strncpy(str, m_submaster.c_str(), sizeof(str)); // connect to submaster m_fd = mscb_init(str, sizeof(str), m_password.c_str(), m_debug); if (m_fd == EMSCB_WRONG_PASSWORD) mthrow("Wrong password for MSCB submaster \"" + m_submaster + "\""); if (m_fd == EMSCB_COMM_ERROR) mthrow("Cannot communicate with MSCB submaster \"" + m_submaster + "\""); if (m_fd == EMSCB_PROTOCOL_VERSION) mthrow("Submaster \"" + m_submaster + "\" runs old protocol version, please upgrade"); if (m_fd == EMSCB_NOT_FOUND) mthrow("Submaster \"" + m_submaster + "\" not found, please check IP address"); if (m_fd == EMSCB_LOCKED) mthrow("MSCB submaster \\\"\" + m_submaster + \"\\\" is locked by other process"); if (m_fd < 0) mthrow("Submaster \"" + m_submaster + "\" not found, please check IP address"); // get general info MSCB_INFO info; int status = mscb_info(m_fd, m_node_address, &info); if (status != 1) mthrow("Cannot find MSCB node " + m_submaster + ":" + std::to_string(m_node_address)); m_protocol_version = info.protocol_version; m_n_variables = info.n_variables; m_group_address = info.group_address; m_revision = info.revision; m_node_name = std::string(info.node_name); m_rtc = std::string((char *) info.rtc); // get uptime unsigned int uptime; mscb_uptime(m_fd, m_node_address, &uptime); m_uptime = uptime; // read variable definitions for (int i = 0; i < m_n_variables; i++) { MSCB_INFO_VAR info_var; mscb_info_variable(m_fd, m_node_address, i, &info_var); m_var.push_back(info_var); m_map[info_var.name] = i; // set tid from info int tid = 0; if (m_var[i].unit == UNIT_STRING) tid = TID_STRING; else if (m_var[i].flags & MSCBF_FLOAT) tid = TID_FLOAT; else if (m_var[i].flags & MSCBF_SIGNED) { if (m_var[i].width == 1) tid = TID_INT8; else if (m_var[i].width == 2) tid = TID_INT16; else if (m_var[i].width == 4) tid = TID_INT32; } else { if (m_var[i].width == 1) tid = TID_UINT8; else if (m_var[i].width == 2) tid = TID_UINT16; else if (m_var[i].width == 4) tid = TID_UINT32; } u_mscb d(m_fd, m_node_address, i, tid, m_var[i].width); m_data.push_back(d); } read_range(); } // Getter std::string get_node_name() { return m_node_name; } std::string get_submaster() { return m_submaster; } int get_node_address() { return m_node_address; } int get_protocol_version() { return m_protocol_version; } int read_range(int i1 = 0, int i2 = 0) { // read range of variables from i1 to i2 (including) unsigned char data[1024], *pdata; if (i1 == 0 && i2 == 0) i2 = m_n_variables-1; int size = sizeof(data); int status = mscb_read_range(m_fd, m_node_address, i1, i2, data, &size); pdata = data; for (int i = i1; i <= i2; i++) { if (m_var[i].width == 2) WORD_SWAP(pdata); if (m_var[i].width == 4) DWORD_SWAP(pdata); if (m_var[i].unit == UNIT_STRING) m_data[i].set_value(std::string((char *) pdata)); else m_data[i].set_data(pdata); pdata += m_var[i].width; } return status; } // overload stream out operator friend std::ostream &operator<<(std::ostream &output, mscb &m) { output << "Submaster : " << m.m_submaster << std::endl; output << "Node name : " << m.m_node_name << std::endl; output << "Node address : " << m.m_node_address << " (0x" << std::hex << std::uppercase << std::setw(4) << std::setfill('0') << m.m_node_address << ")" << std::dec << std::endl; output << "Group address : " << m.m_group_address << " (0x" << std::hex << std::uppercase << std::setw(4) << std::setfill('0') << m.m_group_address << ")" << std::dec << std::endl; output << "Protocol version : " << m.m_protocol_version << std::endl; output << "Revision : " << m.m_revision << " (0x" << std::hex << std::uppercase << std::setw(4) << std::setfill('0') << m.m_revision << ")" << std::dec << std::endl; output << "Uptime : "; output << m.m_uptime / (3600 * 24) << "d "; output << std::setw(2) << std::setfill('0') << (m.m_uptime % (3600 * 24)) / 3600 << "h "; output << std::setw(2) << std::setfill('0') << (m.m_uptime % 3600) / 60 << "m "; output << std::setw(2) << std::setfill('0') << m.m_uptime % 60 << "s "; output << std::endl; for (int i=0 ; i 0) output << prefixMap[m.m_var[i].prefix]; } // evaluate unit if (m.m_var[i].unit && m.m_var[i].unit != UNIT_STRING) { if (unitTable.count(m.m_var[i].unit) > 0) output << unitTable[m.m_var[i].unit]; } output << std::endl; } output << std::dec << std::setw(0) << std::setfill(' '); return output; }; // overload [] operator u_mscb& operator[](int index) { if (index < 0 || index >= m_n_variables) mthrow("Invalid index \"" + std::to_string(index) + "\" for MSCB node \"" + m_node_name + "\" which has only " + std::to_string(m_n_variables) + " variables"); return m_data[index]; } int idx(std::string var_name) { auto it = m_map.find(var_name); if (it == m_map.end()) return -1; else return it->second; } // overload [] operator u_mscb& operator[](std::string var_name) { int index = idx(var_name); if (index == -1) mthrow("Variable \"" + var_name + "\" not found in MSCB node \"" + m_node_name + "\""); return m_data[index]; } std::vector vec(int i1, int i2) { std::vector v; for (int i = i1 ; i<=i2 ; i++) v.push_back(m_data[i]); return v; } }; } #endif