PortChecker.cpp (8205B)
1 #include <cassert> 2 #include <queue> 3 #include <thread> 4 #include <mutex> 5 #include <iostream> 6 #include <ctime> 7 #include <unistd.h> 8 #include <rtosc/thread-link.h> 9 #include <rtosc/rtosc-time.h> 10 #include <rtosc/port-checker.h> 11 #include <liblo-server.h> // from rtosc's test directory 12 13 #include "../Misc/Master.h" 14 #include "../Misc/MiddleWare.h" 15 #include "../UI/NSM.H" 16 #include "rtosc/arg-val.h" 17 18 // for linking purposes only: 19 NSM_Client *nsm = 0; 20 zyn::MiddleWare *middleware = 0; 21 22 char *instance_name=(char*)""; 23 24 //#define DEBUG_PORT_CHECKER 25 26 //! non-network implementation of port_checker::server 27 //! @note This is all in one thread - thus, no mutexes etc 28 class direct_server : public rtosc::port_checker::server 29 { 30 zyn::MiddleWare* const mw; 31 const std::size_t gui_id; 32 std::queue<std::vector<char>> received; // inbox for MW's replies 33 34 public: 35 //!< UI callback, will queue message in `received` 36 void on_recv(const char* message) 37 { 38 int len = rtosc_message_length(message, -1); 39 std::vector<char> buf(len); 40 memcpy(buf.data(), message, len); 41 received.push(std::move(buf)); 42 } 43 44 //!< Fetch exactly 1 msg from `received`, may call server::handle_recv 45 void fetch_received() 46 { 47 if(!received.empty()) 48 { 49 const char* message = received.front().data(); 50 if(waiting && exp_paths_n_args[0].size()) 51 { 52 _replied_path = 0; 53 for(std::vector<const char*>* exp_strs = exp_paths_n_args; exp_strs->size(); 54 ++exp_strs, ++_replied_path) 55 if(!strcmp((*exp_strs)[0], message)) 56 { 57 #ifdef DEBUG_PORT_CHECKER 58 std::cout << "on_recv: match:" << std::endl; 59 #endif 60 int len = rtosc_message_length(message, -1); 61 62 *last_buffer = std::vector<char>(len); 63 memcpy(last_buffer->data(), message, len); 64 65 unsigned nargs = rtosc_narguments(message); 66 std::vector<char> types(nargs); 67 for (unsigned i = 0; i < nargs; ++i) 68 types[i] = rtosc_type(message, i); 69 70 server::handle_recv(nargs, types.data(), exp_strs); 71 break; 72 } 73 } 74 received.pop(); 75 } 76 } 77 78 //! Let port checker send a message to MW 79 bool send_msg(const char* address, 80 size_t nargs, const rtosc_arg_val_t* args) override 81 { 82 char buf[8192]; 83 int len = rtosc_avmessage(buf, sizeof(buf), address, nargs, args); 84 if(len <= 0) 85 throw std::runtime_error("Could not serialize message"); 86 mw->transmitMsgGui(gui_id, buf); 87 return true; 88 } 89 90 //! Let port checker wait until matching message was replied 91 bool _wait_for_reply(std::vector<char>* buffer, 92 std::vector<rtosc_arg_val_t> * args, 93 int n0, int n1) override 94 { 95 (void)n0; 96 // TODO: most of this function is common to liblo_server 97 // => refactor into portchecker 98 assert(n1); 99 exp_paths_n_args[n1].clear(); 100 101 last_args = args; 102 last_buffer = buffer; 103 104 // allow up to 1000 recv calls = 1s 105 // if there's no reply at all after 0.5 seconds, abort 106 const int timeout_initial = timeout_msecs; 107 int tries_left = 1000, timeout = timeout_initial; 108 while(tries_left-->1 && timeout-->1 && waiting) // waiting is set in "on_recv" 109 { 110 mw->tick(); 111 usleep(1000); 112 if(received.size()) { 113 fetch_received(); 114 timeout = timeout_initial; 115 } 116 // message will be dispatched to the server's callback 117 // server::handle_recv will unset `waiting` if a "good" message was found 118 } 119 waiting = true; // prepare for next round 120 121 return tries_left && timeout; 122 } 123 124 void vinit(const char* ) override {} // nothing to do here 125 126 direct_server(std::size_t gui_id, int timeout_msecs, zyn::MiddleWare* mw) 127 : server(timeout_msecs) 128 , mw(mw) 129 , gui_id(gui_id) {} 130 }; 131 132 class PortChecker 133 { 134 void _masterChangedCallback(zyn::Master* m) 135 { 136 master = m; 137 master->setMasterChangedCallback( 138 [](void* p, zyn::Master* m) { 139 ((PortChecker*)p)->_masterChangedCallback(m); }, 140 this); 141 } 142 143 void setUp() { 144 // this might be set to true in the future 145 // when saving will work better 146 config.cfg.SaveFullXml = false; 147 148 synth = new zyn::SYNTH_T; 149 synth->buffersize = 256; 150 synth->samplerate = 48000; 151 synth->oscilsize = 256; 152 synth->alias(); 153 154 mw = new zyn::MiddleWare(std::move(*synth), &config); 155 mw->setUiCallback(0, 156 [](void* p, const char* msg) { 157 ((PortChecker*)p)->uiCallback0(msg); }, 158 this); 159 mw->setUiCallback(1, 160 [](void* p, const char* msg) { 161 ((PortChecker*)p)->uiCallback1(msg); }, 162 this); 163 _masterChangedCallback(mw->spawnMaster()); 164 realtime = nullptr; 165 } 166 167 void tearDown() { 168 #ifdef SAVE_OSC_DEBUG 169 printf("Master at the end: %p\n", master); 170 #endif 171 delete mw; 172 delete synth; 173 } 174 175 void uiCallback0(const char* msg) 176 { 177 sender->on_recv(msg); 178 } 179 180 void uiCallback1(const char* msg) 181 { 182 other->on_recv(msg); 183 } 184 185 public: 186 PortChecker() { setUp(); } 187 ~PortChecker() { tearDown(); } 188 189 int run() 190 { 191 assert(mw); 192 193 bool ok; 194 try { 195 int timeout_msecs = 50; 196 sender = new direct_server(0u, timeout_msecs, mw); 197 other = new direct_server(1u, timeout_msecs, mw); 198 rtosc::port_checker pc(sender, other); 199 ok = pc(mw->getServerPort()); 200 201 if(!pc.print_sanity_checks()) 202 ok = false; 203 pc.print_evaluation(); 204 if(pc.errors_found()) 205 ok = false; 206 pc.print_not_affected(); 207 pc.print_skipped(); 208 pc.print_statistics(); 209 210 } catch(const std::exception& e) { 211 std::cerr << "**Error caught**: " << e.what() << std::endl; 212 ok = false; 213 } 214 215 int res = ok ? EXIT_SUCCESS : EXIT_FAILURE; 216 return res; 217 } 218 219 void start_realtime(void) 220 { 221 do_exit = false; 222 223 realtime = new std::thread([this](){ 224 while(!do_exit) 225 { 226 if(master->uToB->hasNext()) { 227 const char *msg = master->uToB->read(); 228 #ifdef ZYN_PORT_CHECKER_DEBUG 229 printf("Master %p: handling <%s>\n", master, msg); 230 #endif 231 master->applyOscEvent(msg, false); 232 } 233 else { 234 // RT has no incoming message? 235 if(do_exit) 236 break; 237 usleep(500); 238 } 239 master->last_ack = master->last_beat; 240 }}); 241 } 242 243 void stop_realtime(void) 244 { 245 do_exit = true; 246 247 realtime->join(); 248 delete realtime; 249 realtime = NULL; 250 } 251 252 private: 253 zyn::Config config; 254 zyn::SYNTH_T* synth; 255 zyn::Master* master = NULL; 256 zyn::MiddleWare* mw; 257 std::thread* realtime; 258 bool do_exit; 259 direct_server* sender, * other; 260 }; 261 262 int main() 263 { 264 PortChecker test; 265 test.start_realtime(); 266 int res = test.run(); 267 test.stop_realtime(); 268 std::cerr << "Summary: " << ((res == EXIT_SUCCESS) ? "SUCCESS" : "FAILURE") 269 << std::endl; 270 return res; 271 } 272 273