Esempio n. 1
0
 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
Esempio n. 2
0
 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()
Esempio n. 3
0
 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
Esempio n. 4
0
 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()
Esempio n. 5
0
 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 = []
Esempio n. 6
0
 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()
Esempio n. 7
0
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)
Esempio n. 8
0
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)
Esempio n. 9
0
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)
Esempio n. 10
0
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)