def __init__(self, name, network): """ kind of constructor for node protocol. """ self.cm = ComputationalModel() self.node_name = name self.network = network self.all_vectors = {} # dictionary: <node_id:vector> pairs self.incoming_messages = [] # a list of all incoming messages # the list of all nodeclass instances (each contains a node) self.NodeList = [] # state of the node TODO it can be completely removed self.state = None self.mf = MonitoredFunctionImpl() # TODO must be given by user
def __init__(self, name, network): """ kind of constructor for node protocol. """ self.cm = ComputationalModel() self.ams = AMS_Sketch() self.node_name = name self.network = network self.incoming_messages = [] # a list of all incoming messages self.COORD = None # coordinator's name self.weight = None self.ack = True # ack for a successful message sending # state of the node self.state = 'INIT' # bit of hardcoded self.mf = MonitoredFunctionImpl(len(self.ams.sketch)) self.state_mon = Monitor()
def __init__(self, node_name, network): """ inits the protocol for a particular node. """ self.node_name = node_name self.network = network self.cm = ComputationalModel() self.all_vectors = {} self.incoming_messages = [] self.COORD = '' # init coord to null # special ack is True if node waits for a NEW_EST or ADJ_SLK msg self.special_ack = '' self.weight = 0 # init the weight to zero. self.NodeList = [] # list of all nodeclass instances self.mf = MonitoredFunctionImpl()
def __init__(self, node_name, network): """ inits the coordinator. """ self.node_name = node_name self.network = network self.cm = ComputationalModel() self.all_vectors = {} self.incoming_messages = [] # initialize the balancing process with the coordinatorProcess self.bp = BalancingProcess(self) # a list containing messages with results from balancing process self.balancing_msgs = [] self.weight = 0 # weight for coord, too self.mf = MonitoredFunctionImpl() self.NodeList = []
class NodeProtocol(Protocol): """ This class implements the protocol from node's side. """ def __init__(self, name, network): """ kind of constructor for node protocol. """ self.cm = ComputationalModel() self.node_name = name self.network = network self.all_vectors = {} # dictionary: <node_id:vector> pairs self.incoming_messages = [] # a list of all incoming messages # the list of all nodeclass instances (each contains a node) self.NodeList = [] # state of the node TODO it can be completely removed self.state = None self.mf = MonitoredFunctionImpl() # TODO must be given by user ## API ------------------------------------------------------------ def sendMessage(self, msg): """ sends a message to appropriate node. """ # call environment function here self.network.addMsgToQueue(msg) def bcastStatVector(self, vector): """ Broadcasts the initial statistics vector to the other nodes. """ # create a list with all id's self.id_list = self.getAllNodesId(self.NodeList) # create the message with the list of nodes as recipients self.msg = self.createMessage(vector, self.id_list) # and send it self.sendMessage(self.msg) def recvMessage(self, msg): """ receives a message. """ self.incoming_messages.append(msg) def set_state(self, state): """ sets the state of the system. """ self.state = state ## Gather new data, check, and send ------------------------------- def getDataFromNode(self): """ gets data from node and returns it""" self.my_node = self.getMyNode() self.data = self.my_node.gatherData() return self.data def getMyNode(self): """ returns the node where this instance of protocol runs. """ #self.my_node = [item for item in self.NodeList \ # if item.node.name == self.node_name][0] #return self.my_node.node self.my_node = None for item in self.NodeList: if item.node.name == self.node_name: self.my_node = item.node return self.my_node return self.my_node def createMessage(self, content, recp): """ creates a message for sending """ self.new_msg = Message(self.node_name, recp, content) return self.new_msg ## Initialization phase of protocol ------------------------------- def getAllNodesId(self, nl): """ returns a list with the id's of all nodes. """ # init an empty list self.id_list = [] # for every node, take the id and append it to list for node_class in nl: self.id_list.append(node_class.node.name) return self.id_list def recvStatVector(self, vector): """ receives the local statistics vector from other nodes. """ self.vector = vector return vector def updateStatVector(self, node_id, vector): """ updates the local statistics vector from other nodes. """ if node_id in self.all_vectors: self.all_vectors[node_id][1] = vector else: print "Error here! This id doesn't exist in all_vectors list!" def calcEstimate(self): """ calculates estimate vector for first time. """ # while there is a message in incoming_messages list, while self.incoming_messages: # remove it from list self.msg = self.incoming_messages.pop(0) # process the message and extract the id and the vector self.nodeID, self.vec = self.processMessage(self.msg) # process it and add it to all_vectors dictionary self.updateStatVector(self.nodeID, self.vec) # now calculate estimate vector for this node self.recalcEstimateVector() def recalcEstimateVector(self): """ calculates the estimate vector. """ # init sum of weights in the minimum permitted value # (so as to avoid division with zero) self.sumw = 0.000000000001 # for every single node for node in self.all_vectors: # get the weight and the last statistic vector self.w = float(self.all_vectors[node][0]) self.v = self.all_vectors[node][1] # compute the denominator self.sumw += self.w # and compute the numerator self.cm.estimate = VectorOps.addTo(self.cm.estimate, VectorOps.mult(self.v, self.w)) # calculate the final estimate vector self.cm.estimate = VectorOps.multBy\ (self.cm.estimate, 1.0/self.sumw) def calcDriftVector(self): """ calculates drift vector. Drift vector is the following vector: u(t) = e(t) + Δv(t), where Δv(t) = v(t) - v'(t) """ # calculate Δv(t) self.dv = VectorOps.subFrom(self.cm.localStatistics, \ self.cm.lastStatistics) # calculate drift vector self.cm.drift = VectorOps.addTo(self.cm.estimate,\ self.dv) def recalcLocalStatistics(self, new_vector): """ recalculates local statistics vector. """ self.cm.localStatistics = new_vector def processMessage(self, msg): """ processes a message. This function takes the node's id from the message and the data (different approach if this is a signal or a vector) """ # first, learn who the sender is self.node_id = msg.sender # check if this is a vector or a signal if self.isSignal(msg.content) == False: # if it's a vector, then receive the vector self.vector = self.recvStatVector(msg.content) # and return both node_id and vector # so as to process the node's data return (self.node_id, self.vector) def isSignal(self, content): """ checks if the content is a known signal or data. """ if content == 'SKATILA': return True else: return False ## Following functions are called top-level def initProtocol(self, node_list): """ inits the protocol and the very basic variables and vectors. @param node_list is the list that contains all nodes. """ self.setNodeList(node_list) self.initAllVectorsList() def setNodeList(self, nl): """ sets the list of all nodes """ #for item in nl: # self.NodeList.append(item.node) self.NodeList = nl def initAllVectorsList(self): """ inits the all_vectors vector with node id and weight. """ for nc in self.NodeList: self.all_vectors[nc.node.name] = [nc.node.getWeight(), []] ## Initialization ------------------------------------------------- def initialization(self): """ runs the initialization of the protocol. """ # get data from the environment self.data_vector = self.getDataFromNode() # assign the data to local statistics vector self.cm.localStatistics = self.data_vector # and broadcast the vector self.bcastStatVector(self.data_vector) ## Adjustment messages TODO Fill in some code here ---------------- ## Processing Stage ----------------------------------------------- def recvLocalUpdate(self, local_stats_vec): """ receives a new local statistics vector from stream. """ # recalculate local statistics vector self.recalcLocalStatistics(local_stats_vec) # recalculate drift vector self.calcDriftVector() # check if the ball remains monochromatic and # send a message to all if necessary self.checkBall(local_stats_vec) def recvNewMessage(self): """ receives a new message. """ while self.incoming_messages: # take the new message self.msg = self.incoming_messages.pop(0) # process the message and extract the id and the vector self.nodeID, self.vec = self.processMessage(self.msg) # update this node's last statistics vector to hold the new one self.updateStatVector(self.nodeID, self.vec) # recalculate estimate vector e(t) self.recalcEstimateVector() # check if the ball is monochromatic and # send a message with the local statistics vector to all nodes, # if necessary self.checkBall(self.cm.localStatistics) def checkBall(self, local_stats_vec): """ checks if the ball remains monochromatic. """ if not self.mf.isMonochromatic(self.cm.estimate, self.cm.drift): self.cm.lastStatistics = local_stats_vec def dontSend(self): """ returns true or false, depending on isMonochromatic answer. if it's true, then send message else not.""" return self.mf.isMonochromatic(self.cm.estimate, self.cm.drift)
class NodeProtocol(Protocol): """ This class implements the protocol from node's side. """ def __init__(self, name, network): """ kind of constructor for node protocol. """ self.cm = ComputationalModel() self.ams = AMS_Sketch() self.node_name = name self.network = network self.incoming_messages = [] # a list of all incoming messages self.COORD = None # coordinator's name self.weight = None self.ack = True # ack for a successful message sending # state of the node self.state = 'INIT' # bit of hardcoded self.mf = MonitoredFunctionImpl(len(self.ams.sketch)) self.state_mon = Monitor() #self.state_mon.observe(0) ## API ------------------------------------------------------------ def sendMessage(self, msg): """ sends a message to appropriate node. """ # call environment function here self.network.addMsgToQueue(msg) # and wait for an acknowledgement self._set_ack(False) def bcastStatVector(self, vector): """ Broadcasts the initial statistics vector to the other nodes. """ # create a list with all id's self.id_list = self.getAllNodesId(self.NodeList) # create the message with the list of nodes as recipients self.msg = self.createMessage(vector, self.id_list) # and send it self.sendMessage(self.msg) def recvMessage(self, msg): """ receives a message. """ if not self.is_ack(msg): self.incoming_messages.append(msg) self.process_message() def is_ack(self, msg): """ checks if the msg is an ack. @param msg is the message. """ # if the message has attribute signal and it is ACK if hasattr(msg, 'signal'): if msg.signal == 'ACK': # set ack=True and return True self._set_ack(True) return True # else return False return False def set_state(self, state): """ sets the state of the system. """ self.state = state if self.state == 'SAFE': self.state_mon.observe(1.0) else: self.state_mon.observe(0.0) #self.state_mon.observe(self.state) def set_coord(self, coord_name): """ sets the coord. """ self.coord = coord_name def set_weight(self, weight): """ sets the weight of a node. """ self.weight = weight ## Initialization of the protocol -------------------------------- def start(self, raw_data, weight): """ starts the protocol. Initialization phase of the coordinator-based algorithm is implemented, exactly as it is presented in the paper [lift2006]. @param raw_data is a list that contains items of unprocessed data. """ # first of all, set the weight of the node to protocol self.set_weight(weight) # read all waiting, raw data and pass it to a sketch self._read_data(raw_data) # update last statistics vector to hold the localstats self._set_lastStatistics(self.ams.sketch) # update slack vector to 0 VectorOps.init_empty_vector(self.cm.slack, \ self.cm.lastStatistics) # create an init message self.init_msg = self.create_init_msg() # send an 'INIT'-type message to coordinator self.sendMessage(self.init_msg) # finally set change state self.set_state('UNSAFE') def create_init_msg(self): """ creates and returns an 'INIT'-type message. """ assert self.COORD, "no coordinator has been defined!" self.init_msg = INIT(self.node_name, self.COORD, self.cm.lastStatistics, self.weight) return self.init_msg ## Local arrival (1st phase of processing stage) ----------------- def recv_local_update(self, raw_data): """ receives a new local statistics vector and processes it. @param localStats is a local statistics vector """ # read the waiting, raw data and update local statistics self._read_data(raw_data) # recalculate drift vector self.calc_drift() def calc_drift(self): """ calculates drift vector. Drift vector is computed as follows: u(t) = e(t) + delta-v(t), where delta-v(t) = v(t) = v'(t) """ # calculate delta-v self.dv = VectorOps.subFrom(self.cm.localStatistics,\ self.cm.lastStatistics) # calculate drift vector self.cm.drift = VectorOps.addTo(self.cm.estimate, self.dv) # now check if ball is monochromatic if not self.check_ball(self.cm.localStatistics): # if it is not, then send a REP-message to coordinator # (the algorithm here is exactly the same as in REQ_receipt()) self.REQ_receipt() def check_ball(self, localStats): """ checks if the ball is monochromatic. If it is not, then update lastStatistics with localStatistics vector and return False. Else, return True. @param localStats is the local statistics vector. """ if not self.mf.isMonochromatic(self.cm.estimate, self.cm.drift): self.cm.lastStatistics = localStats return False return True def REQ_receipt(self): """ Upon receipt of a REQ message, send a REP(v(t), u(t)) message to the coordinator and wait for either a NEW_EST or an ADJ_SLK message. """ # set the state to unsafe self.set_state('UNSAFE') # calculate drift vector #self.calc_drift() # create a REP message self.msg = REP(self.node_name, self.COORD,\ self.cm.localStatistics, self.cm.drift) # and send it self.sendMessage(self.msg) #self.waitSpecialAck() # TODO Implement this ## New message process ------------------------------------------- def process_message(self): """ processes a new message. """ while self.incoming_messages: # extract the new message self.msg = self.incoming_messages.pop(0) # and examine the different signal types if self.msg.signal == 'REQ': self.REQ_receipt() break elif self.msg.signal == 'NEW_EST': self.NEW_EST_receipt(self.msg) break elif self.msg.signal == 'ADJ_SLK': self.ADJ_SLK.receipt(self.msg) break elif self.msg.signal == 'ACK': self._set_ack(True) break else: print 'Message error: No such message(nodeprotocol)' # finally, a little test about acks: if self.msg.signal != 'ACK': # if message is NOT an ack, then send an ACK! self._send_ACK(self.node_name, self.COORD) def NEW_EST_receipt(self, message): """ Upon receipt of a NEW_EST message, update the estimate vector e(t) to the value specified in the message, set the value of v' to the statistics vector sent to the coordinator, and set the slack vector to 0. """ # first of all set state self.set_state('SAFE') self.cm.estimate = message.content self.update_threshold(message.content) self.cm.lastStatistics = self.cm.localStatistics self.cm.slack = [0] * len(message.content) def update_threshold(self, threshold): """ updates the threshold according to the received message and to the already existed threshold. @param threshold is the new threshold. """ self.mf.set_threshold(self.cm.estimate) def ADJ_SLK_receipt(self, message): """ Upon receipt of an ADJ_SLK message, add the value specified in the message to the value of the slack vector (δ <- δ + Δδ). """ self.cm.slack = VectorOps.addTo(self.cm.slack, message.content) self.set_state('SAFE') ## functions used in wide range ---------------------------------- def _read_data(self, raw_data): """ reads data from a list, until the list is empty. @param raw_data is a list full of data. """ # read all new data and add it to sketch while raw_data: self.data = raw_data.pop(0) self.ams.update(self.data) # update local, last and slack statistics vectors self.cm.localStatistics = self.ams.sketch def _set_lastStatistics(self, vec): """ sets the last staticics vector. @param vec is the vector to which lastStats vector will be updated. """ self.cm.lastStatistics = vec def _set_ack(self, ack=True): """ sets the ack True or False, depending on whether an ACK has been received or not. @param ack is the ACKNOWLEDGEMENT. """ self.ack = ack def wait_ack(self): """ this method checks if an ack has come. @return True if we wait for an ack and false otherwise. """ if not self.ack: return False return True def _send_ACK(self, sender='', recipient=''): """ sends an ACKNOWLEDGEMENT signal. @param node_name is the name of the node that sends the ACK @param recipient is the name of the node to which the ACK is sent. """ # create an ACK message self.ack_msg = ACK(sender, recipient) # be sure that sender AND recipient do exist assert self.ack_msg.sender and self.ack_msg.recipient,\ "no ack_msg.sender or recipient exist (_send_ACK())" # and send the ACK self.sendMessage(self.ack_msg)