void trigger(void *cvoid, zctx_t * context, void * control) { triggerconfig_t * c = (triggerconfig_t*) cvoid; //set up msgpack stuff zclock_log("watch_port started!"); msgpack_zone mempool; msgpack_zone_init(&mempool, 2048); // TODO char * user_id = "17"; // TODO get broker in somehow char * broker = "tcp://au.ninjablocks.com:5773"; mdcli_t * client = mdcli_new(broker, 1); //VERBOSE triggermemory_t trigger_memory; msgpack_object * addins_obj = parse_msgpack(&mempool, c->addins); if(!parse_addins(addins_obj, &trigger_memory)) { //bad message zclock_log("bad trigger definition"); msgpack_object_print(stdout, *addins_obj); send_sync("bad trigger", control); return; } zclock_log("Creating trigger: target %s, rule_id %s, name %s", c->target_worker, c->rule_id, c->trigger_name); dump_trigger(&trigger_memory); triggerfunction trigger_func; if(!(trigger_func = find_trigger(c->channel, c->trigger_name))) { zclock_log("no trigger found for channel %s, trigger %s", c->channel, c->trigger_name); send_sync("no such trigger", control); return; } void * line = zsocket_new(context, ZMQ_SUB); // what line are we on? // this comes in the addins. char * linesocket = to_linesocket(trigger_memory.line_id); zclock_log("trigger is connecting to listen on %s", linesocket); zsocket_connect(line, linesocket); zsockopt_set_unsubscribe(line, ""); zsockopt_set_subscribe(line, "VALUE"); recv_sync("ping", control); send_sync("pong", control); zmq_pollitem_t items [] = { { line, 0, ZMQ_POLLIN, 0 }, { control, 0, ZMQ_POLLIN, 0 } }; while(1) { // listen on control and line zmq_poll (items, 2, -1); if (items[1].revents & ZMQ_POLLIN) { zclock_log("rule %s received message on control pipe", c->rule_id); // control message // really only expecting DESTROY zmsg_t * msg = zmsg_recv(control); char * str = zmsg_popstr(msg); zmsg_destroy(&msg); if (strcmp("Destroy", str) == 0) { zclock_log("rule %s will quit on request", c->rule_id); free(str); send_sync("ok", control); zclock_log("rule %s quitting on request", c->rule_id); break; } else { zclock_log("unexpected command %s for rule %s", str, c->rule_id); free(str); send_sync("ok", control); } } if (items[0].revents & ZMQ_POLLIN) { // serial update zmsg_t * msg = zmsg_recv(line); zframe_t * cmd = zmsg_pop(msg); if(zframe_streq(cmd, "CHANNEL_CHANGE")) { // TODO // must have been dormant to have gotten this char * new_channel = zmsg_popstr(msg); if(strcmp(c->channel, new_channel) == 0) { // oh, happy day! We're relevant again. // reactivate and start looking at reset levels. zclock_log("line %d: changed channel from %s to %s: trigger coming back to life", trigger_memory.line_id, c->channel, new_channel); zsockopt_set_subscribe(line, "VALUE"); zsockopt_set_unsubscribe(line, "CHANNEL_CHANGE"); } free(new_channel); } else if (zframe_streq(cmd, "VALUE")) { zframe_t * vframe = zmsg_pop(msg); int value; memcpy(&value, zframe_data(vframe), sizeof(int)); char * update_channel = zmsg_popstr(msg); if(strcmp(c->channel, update_channel) != 0) { // channel changed, go dormant // this is legit according to my tests at // https://gist.github.com/2042350 zclock_log("line %d: changed channel from %s to %s: trigger going dormant", trigger_memory.line_id, c->channel, update_channel); zsockopt_set_subscribe(line, "CHANNEL_CHANGE"); zsockopt_set_unsubscribe(line, "VALUE"); } else if(trigger_func(&trigger_memory, value)) { send_trigger(client, c->target_worker, c->rule_id, value, user_id); } free(update_channel); } else { // shouldn't ever happen. zclock_log("shouldn't have received command %s\n", zframe_strdup(cmd)); } zmsg_destroy(&msg); zframe_destroy(&cmd); } } msgpack_zone_destroy(&mempool); }
void watch_port(void *cvoid, zctx_t * context, void * pipe ) { zclock_log("watch_port started!"); monitorconfig_t * config = (monitorconfig_t*) cvoid; dump_monitorconfig(config); void * linein = zsocket_new(context, ZMQ_SUB); char * listen_socket = to_linesocket(config->line_id); char line_id[16]; snprintf(line_id, 15, "%d", config->line_id); zsocket_connect(linein, listen_socket); zsockopt_set_unsubscribe(linein, ""); zsockopt_set_subscribe(linein, "CLEAR_MONITORS"); zsockopt_set_subscribe(linein, "VALUE"); // have set up subscription, can signal parent that we're ok. child_handshake(pipe); zsocket_destroy(context, pipe); // no longer require pipe void * lineout = zsocket_new(context, ZMQ_PUB); zsocket_connect(lineout, config->out_socket); time_t until = time(NULL) + 60; while(time(NULL)<until) { zmsg_t * msg = zmsg_recv(linein); if(!msg) { zclock_log("monitor quitting!"); return; } zframe_t * cmd = zmsg_pop(msg); if(zframe_streq(cmd, "CLEAR_MONITORS")) { zclock_log("ephemeral monitor quitting"); zmsg_destroy(&msg); zframe_destroy(&cmd); break; } else if (zframe_streq(cmd, "VALUE")) { // TODO perhaps some rate limiting necessary assert(zmsg_size(msg) == 2); zframe_t * value = zmsg_pop(msg); int res = *(int*)zframe_data(value); char * new_channel = zmsg_popstr(msg); if(strcmp(new_channel, config->channel)!=0) { zclock_log("monitor on %d: listening for %s, channel changed to %s quitting", config->line_id, config->channel, new_channel); zmsg_destroy(&msg); zframe_destroy(&cmd); break; } zmsg_t * to_send = zmsg_new(); char buf[1024]; snprintf(buf,1023, "%d", res); zmsg_pushstr(to_send, buf); zmsg_pushstr(to_send, line_id); zmsg_pushstr(to_send, config->source_worker); zclock_log("%s sending line %s -> %s", config->source_worker, line_id, buf); zmsg_send(&to_send, lineout); // don't destroy value frame, now owned by zmsg } // else ignore zmsg_destroy(&msg); zframe_destroy(&cmd); } zclock_log("monitor on %d: listening for %s, expiring naturally", config->line_id, config->channel); //cleanup zsocket_destroy(context, linein); zsocket_destroy(context, lineout); }
void line_listener(void * cvoid, zctx_t * context, void * pipe) { lineconfig_t * config = (lineconfig_t*) cvoid; // atm, topic == outpipe, but this is coincidental... zmsg_t * msg; dump_lineconfig(config); channel_memory_t channel_memory = { strdup("unknown"), strdup("unknown"), 0 }; // void * monitor_controller = zsocket_new(config->context, ZMQ_PUB); // zsocket_bind(monitor_controller, "inproc://monitor_controller"); // int trigger_capacity = 1; void * lineout = zsocket_new(context, ZMQ_PUB); char * outpipe = to_linesocket(config->line_id); zclock_log("binding line |%s|", outpipe); zsocket_bind(lineout, outpipe); void * subscriber = zsocket_new(context, ZMQ_SUB); zclock_log("subscribing to line |%d|", config->line_id); zsockopt_set_unsubscribe(subscriber, ""); char * topic = to_line(config->line_id); // zsockopt_set_subscribe(subscriber, topic); zclock_log("subscribing to literal line |%s|", topic); zsocket_connect(subscriber, "inproc://line"); child_handshake(pipe); zsocket_destroy(context, pipe); while(1) { msg = zmsg_recv(subscriber); if(!msg) { zclock_log("line quitting!"); return; } // zmsg_dump(msg); char * recv_topic = zmsg_popstr(msg); // zclock_log("line got topic\nreceived: %s\nexpected: %s\n", recv_topic, config->topic); //fflush(stdout); assert(strcmp(recv_topic, topic)==0); free(recv_topic); assert(zmsg_size(msg) == 2); char * channel = zmsg_popstr(msg); zmsg_t * out = zmsg_new(); // originally, I thought it was a neat trick to not mention the // channel in every message. unfortunately, this screws up the // case where a new trigger gets introduced: at the beginning, it // has no idea what the channel currently is. // rather than trying to micro-optimise, let's just keep repeating // the channel in the value update too. if (port_changed(channel, &channel_memory)) { zmsg_pushstr(out, channel_memory.current_channel); zmsg_pushstr(out, "CHANNEL_CHANGE"); zmsg_send(&out, lineout); } // only send a value if we're all settled down if(strcmp(channel, channel_memory.current_channel)==0) { out = zmsg_new(); zmsg_pushstr(out, channel_memory.current_channel); zmsg_push(out, zmsg_pop(msg)); zmsg_pushstr(out, "VALUE"); zmsg_send(&out, lineout); } free(channel); zmsg_destroy(&msg); } free(config); }