mirror of
https://github.com/wassname/catalyst.git
synced 2026-07-08 20:46:47 +08:00
136 lines
5.4 KiB
Python
136 lines
5.4 KiB
Python
import copy
|
|
import multiprocessing
|
|
import zmq
|
|
|
|
import technical as ta
|
|
from core import BaseTransform
|
|
import qsim.util as qutil
|
|
import qsim.messaging as qmsg
|
|
|
|
class MergedTransformsFeed(BaseTransform):
|
|
""" Merge data feed and array of transform feeds into a single result vector.
|
|
PULL from feed
|
|
PULL from child transforms
|
|
PUSH merged message to client
|
|
|
|
"""
|
|
|
|
def __init__(self, feed, props):
|
|
"""
|
|
config - must have an entry for 'transforms':array of dicts, which are
|
|
convertedto configs.
|
|
"""
|
|
BaseTransform.__init__(self, feed, props, "tcp://127.0.0.1:20202")
|
|
self.transform_address = "tcp://127.0.0.1:{port}".format(port=10104)
|
|
self.transform_socket = None
|
|
self.create_transforms(self.config.transforms)
|
|
|
|
|
|
def create_transforms(self, configs):
|
|
"""
|
|
Create transforms based on configs, set each transform's result address to
|
|
this object's transform_address, so that all transformed events will be delivered
|
|
to this object.
|
|
"""
|
|
self.transforms = {}
|
|
for props in configs:
|
|
class_name = props['class']
|
|
if(class_name == 'MovingAverage'):
|
|
mavg = ta.MovingAverage(self.feed, props, self.transform_address)
|
|
self.transforms[mavg.config.name] = mavg
|
|
|
|
keys = copy.copy(self.transforms.keys())
|
|
keys.append("feed") #for the raw feed
|
|
self.data_buffer = qmsg.MergedParallelBuffer(keys)
|
|
|
|
self.buffers = {}
|
|
for name, transform in self.transforms.iteritems():
|
|
self.buffers[name] = []
|
|
|
|
def open(self):
|
|
"""Establish zmq context, feed socket, result socket for client, and transform
|
|
socket to receive transformed events. Create and launch transforms. Will confirm
|
|
ready with the DataFeed at the conclusion."""
|
|
self.context = zmq.Context()
|
|
|
|
qutil.logger.info("starting {name} transform".format(name = self.state['name']))
|
|
#create the feed SUB.
|
|
self.feed_socket = self.context.socket(zmq.SUB)
|
|
self.feed_socket.connect(self.feed.feed_address)
|
|
self.feed_socket.setsockopt(zmq.SUBSCRIBE,'')
|
|
|
|
#create the result PUSH
|
|
self.result_socket = self.context.socket(zmq.PUSH)
|
|
self.result_socket.bind(self.result_address)
|
|
|
|
#create the transform PULL.
|
|
self.transform_socket = self.context.socket(zmq.PULL)
|
|
self.transform_socket.bind(self.transform_address)
|
|
self.data_buffer.out_socket = self.result_socket
|
|
|
|
# Initialize poll set
|
|
self.poller = zmq.Poller()
|
|
self.poller.register(self.feed_socket, zmq.POLLIN)
|
|
self.poller.register(self.transform_socket, zmq.POLLIN)
|
|
|
|
for name, transform in self.transforms.iteritems():
|
|
qutil.logger.info("starting {name}".format(name=name))
|
|
proc = multiprocessing.Process(target=transform.run)
|
|
proc.start()
|
|
|
|
self.sync.confirm()
|
|
|
|
def close(self):
|
|
"""
|
|
Close all zmq sockets and context.
|
|
"""
|
|
self.transform_socket.close()
|
|
BaseTransform.close(self)
|
|
|
|
def process_all(self):
|
|
"""
|
|
Uses a Poller to receive messages from all transforms and the feed.
|
|
All transforms corresponding to the same event are merged with each other
|
|
and the original feed event into a single message. That message is then
|
|
sent to the result socket.
|
|
"""
|
|
done_count = 0
|
|
while True:
|
|
socks = dict(self.poller.poll())
|
|
|
|
if self.feed_socket in socks and socks[self.feed_socket] == zmq.POLLIN:
|
|
message = self.feed_socket.recv()
|
|
if(message == "DONE"):
|
|
qutil.logger.info("finished receiving feed to merge")
|
|
done_count += 1
|
|
else:
|
|
self.received_count += 1
|
|
event = json.loads(message)
|
|
self.data_buffer.append("feed",event)
|
|
|
|
if self.transform_socket in socks and socks[self.transform_socket] == zmq.POLLIN:
|
|
t_message = self.transform_socket.recv()
|
|
if(t_message == "DONE"):
|
|
qutil.logger.info("finished receiving a transform to merge")
|
|
done_count += 1
|
|
else:
|
|
self.received_count += 1
|
|
t_event = json.loads(t_message)
|
|
self.data_buffer.append(t_event['name'], t_event)
|
|
|
|
if(done_count >= len(self.data_buffer)):
|
|
break #done!
|
|
|
|
self.data_buffer.send_next()
|
|
|
|
qutil.logger.info("Transform {name} received {r} and sent {s}".format(name=self.state['name'], r=self.data_buffer.received_count, s=self.data_buffer.sent_count))
|
|
qutil.logger.info("about to drain {n} messages from merger's buffer".format(n=self.data_buffer.pending_messages()))
|
|
|
|
#drain any remaining messages in the buffer
|
|
self.data_buffer.drain()
|
|
|
|
#signal to client that we're done
|
|
self.result_socket.send("DONE")
|
|
qutil.logger.info("Transform {name} received {r} and sent {s}".format(name=self.state['name'], r=self.data_buffer.received_count, s=self.data_buffer.sent_count))
|
|
|