Esempio n. 1
0
 def __init__(self, config_path, **kwargs):
     super(PublisherAgent2, self).__init__(**kwargs)
     self._config = load_config(config_path)
     
     self._src_file_handle = open(settings.source_file)
     header_line = self._src_file_handle.readline().strip()
     self._headers = header_line.split(',')
Esempio n. 2
0
    def __init__(self, config_path, **kwargs):
        super(AppLauncherAgent, self).__init__(**kwargs)
        self.config = utils.load_config(config_path)
        # self.app_number = 0
        #connect to the database
        try:
            self.con = psycopg2.connect(host=db_host, port=db_port, database=db_database, user=db_user,
                                        password=db_password)
            self.cur = self.con.cursor()  # open a cursor to perform database operations
            print("AppLauncher Agent connects to the database name {} successfully".format(db_database))
        except:
            print("ERROR: {} fails to connect to the database name {}".format(app_name, db_database))

        self.time_applauncher_start = datetime.datetime.now()
        self.already_started_previous_apps = False
Esempio n. 3
0
     def __init__(self, **kwargs):
         super(Agent, self).__init__(**kwargs)
         config = utils.load_config(config_path)
         self._connection_params = {
                 key.upper(): val for key, val in
                 config.get('connection', {}).iteritems()}
         for key in ['server', 'database', 'uid', 'pwd']:
             if key in kwargs:
                 self._connection_params[key.upper()] = kwargs.pop(key)
         self._last = {}
 
         self._start_time = get_config('start_time')
 
         # Set defaults then do immediate update based on the default time in the config
 
         self.set_defaults()
         self.do_update()
Esempio n. 4
0
    def TestAgent(config_path, condition, **kwargs):
        config = utils.load_config(config_path)
        agent_id = config['agentid']
        rtu_path = dict((key, config[key])
                        for key in ['campus', 'building', 'unit'])

        class Agent(PublishMixin, BaseAgent):
            def __init__(self, **kwargs):
                super(Agent, self).__init__(**kwargs)
                
            def setup(self):
                super(Agent, self).setup()
                self.damper = 0
                with condition:
                    condition.notify()                

            @matching.match_regex(topics.ACTUATOR_LOCK_ACQUIRE() + '(/.*)')
            def on_lock_result(self, topic, headers, message, match):
                _log.debug("Topic: {topic}, {headers}, Message: {message}".format(
                        topic=topic, headers=headers, message=message))
                self.publish(topics.ACTUATOR_LOCK_RESULT() + match.group(0),
                             headers, jsonapi.dumps('SUCCESS'))

            @matching.match_regex(topics.ACTUATOR_SET() + '(/.*/([^/]+))')
            def on_new_data(self, topic, headers, message, match):
                _log.debug("Topic: {topic}, {headers}, Message: {message}".format(
                        topic=topic, headers=headers, message=message))
                if match.group(2) == 'Damper':
                    self.damper = int(message[0])
                self.publish(topics.ACTUATOR_VALUE() + match.group(0),
                             headers, message[0])

            @periodic(5)
            def send_data(self):
                data = {
                    'ReturnAirTemperature': 55,
                    'OutsideAirTemperature': 50,
                    'MixedAirTemperature': 45,
                    'Damper': self.damper
                }
                self.publish_ex(topics.RTU_VALUE(point='all', **rtu_path),
                                {}, ('application/json', jsonapi.dumps(data)))

        Agent.__name__ = 'TestAgent'
        return Agent(**kwargs)
Esempio n. 5
0
def AhpAgent(config_path, **kwargs):
    config = utils.load_config(config_path)
    agent_id = config['agentid']
    threshold = 14

    def get_config(name):
        try:
            value = kwargs.pop(name)
            return value
        except KeyError:
            return config[name]

    # This agent uses the implementation of the ahp agorithm to determine what (if any)
    # devices in a building are to be curtailed to shed electrical load. The criteria
    # matrix is expected to be in a table in an excel spredsheet (to simplify data entry by the end user)
    # The agent will listen for information being sent from the bacnet drivers regarding the desired
    # devices, and on a periodic basis (to be set in the configuration file) perform calculations to
    # select the device(s) that are candidates for curtailment. This initial implementation will
    # be focused on heat pumps in a commercial building. As a result, the agent will also need
    # to keep track of the times when the compressor was stopped to allow for a period of equilibrium.
    #
    # TODO:
    #   * Have the agent open and read the criteria matrix from the excel spreadsheet
    #   * Subscribe to the topics that provide the information from the bacnet drivers
    #   * Store the readings from the heat pumps that are to be watched

    class Agent(PublishMixin, BaseAgent):
        """Agent that performs curtailment in a building using the AHP algorithm"""
        def __init__(self, **kwars):
            super(Agent, self).__init__(**kwargs)

        def setup(self):
            # Load criteria matrix
            excel_doc = get_config("excel_doc")
            self.output_log = open("ahp_algorithm.log", "w")
            self.logger = LoggerWriter(_log, logging.DEBUG)

            self.logger.write("Testing")

            (self.criteria_labels,
             self.criteria_matrix) = extract_criteria_matrix(excel_doc)
            self.criteria_matrix_sums = calc_column_sums(self.criteria_matrix)

            # Validate criteria matrix
            if (not validate_input(self.criteria_matrix,
                                   self.criteria_matrix_sums,
                                   True,
                                   criteria_labels,
                                   criteria_labelstring,
                                   matrix_rowstring,
                                   display_dest=self.logger)):
                # TODO: log a warning indicatin invalid matrix input
                pass

            # TODO: Load device list. Right now, it will come from the config file.
            #       Eventually this will come from the excel spreadsheet
            self.device_list = get_config('device_list')

            self.deviceLabels = [row[0] for row in self.device_list]
            self.deviceDataHandlers = {}
            for deviceRow in self.device_list:
                self.deviceDataHandlers[deviceRow[0]] = DeviceData(
                    deviceRow[0], deviceRow[1], logger=self.logger)
            super(Agent, self).setup()

        # TODO: Set up subscriptions. Need to subscribe to sigma4/all
        @match_glob('RTU/PNNL/BOCC/Sigma4/HP*/all')
        def process_data(self, topic, headers, message, match):
            data = jsonapi.loads(message[0])
            # print >> self.logger, topic, message

            device_label = topic.split('/')[4]

            if device_label in self.deviceDataHandlers:
                # look up device
                device = self.deviceDataHandlers[device_label]

                # call device process_data method
                device.process_data(time.time(), data)

        @periodic(600)
        def schedule_algorithm(self):
            # submit request for schedule to change points
            headers = {
                'AgentID': agent_id,
                'type': 'NEW_SCHEDULE',
                'requesterID': agent_id,
                'taskID': agent_id,
                'priority': 'LOW_PREEMPT'
            }

            # Build up schedule
            start = str(datetime.datetime.now())
            end = str(datetime.datetime.now() + datetime.timedelta(minutes=1))

            # msg = [['PNNL/BOCC/Sigma4/HP1', start, end]]
            msg = []
            for label in self.deviceLabels:
                msg.append(['PNNL/BOCC/Sigma4/' + label, start, end])

            print >> self.logger, "Submitting schedule"
            self.publish_json(topics.ACTUATOR_SCHEDULE_REQUEST(), headers, msg)
            print >> self.logger, "Schedule submitted"

            # Example from afddagent
            # self.task_timer = self.periodic_timer(60, self.publish_json, topics.ACTUATOR_SCHEDULE_REQUEST(), headers,[["{campus}/{building}/{unit}".format(**rtu_path),self.start,self.end]])

        @match_headers({headers_mod.REQUESTER_ID: agent_id})
        @match_exact(topics.ACTUATOR_SCHEDULE_RESULT())
        def handle_scheduler_response(self, topic, headers, message, match):
            msg = jsonapi.loads(message[0])
            response_type = headers.get('type', 0)

            if response_type == 'NEW_SCHEDULE':
                if msg.get('result', 0) == 'SUCCESS':
                    self.logger.write("Schedule Successful")
                    self.ready = True

        @match_headers({headers_mod.REQUESTER_ID: agent_id})
        @match_start('RTU/actuators/schedule/announce')
        def do_algorithm(self, topic, headers, message, match):
            if not (headers[headers_mod.REQUESTER_ID] == agent_id):
                return

            if self.ready:
                self.ready = False
                print >> self.logger, "====== Calculate Curtailment ======"
                # The actual ahp algorithm stuff will happen as part of the response to a successful request for a schedule.

                device_matrix = generateMatrix(self.device_list,
                                               self.deviceDataHandlers)

                print >> self.logger, self.deviceDataHandlers
                scores = demo_ahp(self.criteria_matrix,
                                  device_matrix,
                                  self.deviceLabels,
                                  self.criteria_labels,
                                  criteria_labelstring,
                                  matrix_rowstring,
                                  display_dest=self.logger)
                # def demo_ahp(criteria_matrix, device_matrix, devices, criteria_labels="", criteria_labelstring="", matrix_rowstring="", display_dest=sys.stdout):
                pwr_saved = 0
                device_offsets = []
                for device in scores:
                    if pwr_saved >= threshold:
                        device_offsets.append(0.0)
                    else:
                        if self.deviceDataHandlers[
                                device[0]].curtailWithThreshold():
                            device_offsets.append(3.0)
                        else:
                            device_offsets.append(0.0)

                        pwr_saved += 7

                header = {'requesterID': agent_id}
                device_count = 0
                for (device, score) in scores:
                    path = 'RTU/actuators/set/PNNL/BOCC/Sigma4/' + device + '/Volttron_Temp_Offset'
                    print >> self.logger, 'Updating %s with %d' % (
                        path, device_offsets[device_count])
                    self.publish(path, header,
                                 str(device_offsets[device_count]))
                    device_count += 1

                headers = {
                    'AgentID': agent_id,
                    'type': 'CANCEL_SCHEDULE',
                    'requesterID': agent_id,
                    'taskID': agent_id,
                }

                self.publish_json(topics.ACTUATOR_SCHEDULE_REQUEST(), headers,
                                  [])

            else:
                self.ready = False

    Agent.__name__ = 'AhpAgent'
    return Agent(**kwargs)
Esempio n. 6
0
def PushAgent(config_path, **kwargs):
    
    config = utils.load_config(config_path)
    def get_config(name):
        try:
            value = kwargs.pop(name)
        except KeyError:
            return config[name]

    agent_id = get_config('agentid')
    
    log_path = get_config('log_path')
    periodic_days = get_config('periodic_days')
    
    class Agent(PublishMixin, BaseAgent):
        '''This agent grabs data from a database then pushes that data to 
        sMAP based on the paths returned.
        
        On startup, it sets the latest time for all retrieved points to
        the start_time value. If this is <0 then it uses current time.
        After that, latest time is the value returned by the DB query.
        This has the potential for a problem if data fails to be
        inserted into sMAP since the agent will not request it again.
        
        '''
        #TODO: The agent should verify that data went into sMAP and the 
        #latest time for a point should be based on that, not on the
        # time returned by the DB.
        
        #TODO: Ideally, on startup the agent would try to find the
        #most recent time for each point in sMAP. Not sure there's
        #an easy query to do this through sMAP so may require 
        #backing up time range queries until you find data.
    
        def __init__(self, **kwargs):
            super(Agent, self).__init__(**kwargs)
            config = utils.load_config(config_path)
            self._connection_params = {
                    key.upper(): val for key, val in
                    config.get('connection', {}).iteritems()}
            for key in ['server', 'database', 'uid', 'pwd']:
                if key in kwargs:
                    self._connection_params[key.upper()] = kwargs.pop(key)
            self._last = {}
    
            self._start_time = get_config('start_time')
    
            # Set defaults then do immediate update based on the default time in the config
    
            self.set_defaults()
            self.do_update()
        
            
        
        def setup(self):
            # Always call the base class setup()
            super(Agent, self).setup()
            
        def set_defaults(self):
            conn = Connection(**self._connection_params)
            if (self._start_time < 0):
                self._start_time = time.time()
            
#             _log.debug(DatetimeFromValue(self._start_time))
            
            for chan_id, path, units, latest in conn.get_points():
                stamp = DatetimeFromValue(latest) if latest != None else ''
                
                self._last[chan_id] = self._start_time
                
                _log.debug('{chan}: {path} {units} - {latest}'.format(chan=chan_id, 
                                                     path=path, latest=stamp, units=units))
    
#         @periodic(periodic_days * 24 * 60 * 60)
        @periodic(60 * 60)
        def do_update(self):
            conn = Connection(**self._connection_params)
            for chan_id, path, units, latest in conn.get_points():
                _log.debug(str(chan_id) + " " + path + " " + str(DatetimeFromValue(self._last.get(chan_id))))
                values = conn.get_values(chan_id, self._last.get(chan_id))
                if values:
                    # Send them to sMAP
                    self._last[chan_id] = values[-1][0]
                    
                    time.sleep(3)
                    
                    self.log_to_smap(log_path, path, values, units)
                    
        def log_to_smap(self, log_source, path, values, units="Units"):
            '''Push data to sMAP. This will push data into a path off the 
            main source:  /Source/log_source/path. If logging data is 
            to go right beside real data, log source will need to be removed.
            '''
            _log.debug( path)
            headers = {}
            headers[headers_mod.FROM] = agent_id
            headers[headers_mod.CONTENT_TYPE] = headers_mod.CONTENT_TYPE.JSON
#             headers['SourceName'] = log_source
            
            # Split full path to get path and point name
            path_to_point = path[0:path.rfind('/')]
            point = path[path.rfind('/')+1:len(path)]
            
            content = {
                point: {
                    "Readings": values,
                    "Units": units,
                    "data_type": "double"
                }
            }
            topic = 'datalogger/log/'+log_source+path_to_point
            self.publish(topic, headers, json.dumps(content))
    
        @matching.match_headers({headers_mod.TO: agent_id})
        @matching.match_exact('datalogger/status')
        def on_logger_status(self, topic, headers, message, match):
            if  message != ["Success"]:
                _log.error("Logging attempt failed")
    
    Agent.__name__ = 'SMDSPushAgent'
    return Agent(**kwargs)
Esempio n. 7
0
def DemandResponseAgent(config_path, **kwargs):
    """DR application for time of use pricing"""
    config = utils.load_config(config_path)
    agent_id = config['agentid']
    rtu_path = dict((key, config[key])
                    for key in ['campus', 'building', 'unit'])
    command_timeout = config.get('command-timeout',
                                 settings.default_command_timeout)
    csp_pre = config.get('csp_pre', 
                    settings.csp_pre)
    csp_cpp = config.get('csp_cpp', 
                    settings.csp_cpp)
    damper_cpp = config.get('damper_cpp', 
                    settings.damper_cpp)
    fan_reduction = config.get('fan_reduction',
                    settings.fan_reduction)
    time_steps_perhour = config.get('time_steps_perhour',
                    settings.pre_time)
    
    Schedule = config.get('Schedule')
    
    max_precool_hours = config.get('max_precool_hours')
    
    
    datefmt = '%m-%d-%y %H:%M'
    

    cpp_end_hour = config.get('cpp_end_hour') 
    
    timestep_length = config.get('timestep_length')
    
    building_thermal_constant = config.get('building_thermal_constant')
    
    class Agent(PublishMixin, BaseAgent):
        """Class agent"""

        def __init__(self, **kwargs):
            super(Agent, self).__init__(**kwargs)
            self.default_firststage_fanspeed = 0.0
            self.default_secondstage_fanspeed = 0.0
            self.default_damperstpt = 0.0
            self.default_coolingstpt = 0.0
            self.default_heatingstpt = 65.0
            
            self.current_spacetemp = 72.0
            
            self.state = 'STARTUP'
            self.e_start_msg = None
            self.lock_handler = None
            self.error_handler = None
            self.actuator_handler = None
            
            self.all_scheduled_events = {}
            self.currently_running_dr_event_handlers = []
            self.headers = {headers_mod.CONTENT_TYPE: headers_mod.CONTENT_TYPE.JSON, 'requesterID': agent_id}

        @matching.match_exact(topics.ACTUATOR_LOCK_RESULT(**rtu_path))
        def _on_lock_result(self, topic, headers, message, match):
            """lock result"""
            msg = jsonapi.loads(message[0])
            if headers['requesterID'] == agent_id:
                if msg == 'SUCCESS' and self.lock_handler is not None:
                    self.lock_handler()
                if msg == 'FAILURE' and self.error_handler is not None:
                    self.error_handler(msg)
                    
        @matching.match_glob(topics.ACTUATOR_ERROR(point='*', **rtu_path))
        def _on_error_result(self, topic, headers, message, match):
            """lock result"""
            if headers.get('requesterID', '') == agent_id:
                if self.error_handler is not None:
                    self.error_handler(match, jsonapi.loads(message[0]))
                    
        @matching.match_glob(topics.ACTUATOR_VALUE(point='*', **rtu_path))
        def _on_actuator_result(self, topic, headers, message, match):
            """lock result"""
            msg = jsonapi.loads(message[0])
            print 'Actuator Results:', match, msg                    
            if headers['requesterID'] == agent_id:
                if self.actuator_handler is not None:
                    self.actuator_handler(match, jsonapi.loads(message[0]))

        @matching.match_exact(topics.RTU_VALUE(point='all', **rtu_path))
        def _on_new_data(self, topic, headers, message, match):
            """watching for new data"""
            data = jsonapi.loads(message[0])
#             self.current_spacetemp = float(data["ZoneTemp"])
            self.current_spacetemp = 76
            droveride = bool(int(data["CoolCall2"]))
            occupied = bool(int(data["Occupied"]))
            
            if droveride and self.state not in ('IDLE', 'CLEANUP', 'STARTUP'):
                print 'User Override Initiated'
                self.cancel_event()
            
            if not occupied and self.state in ('DR_EVENT', 'RESTORE'):
                self.cancel_event()
                
            if self.state == 'IDLE' or self.state=='STARTUP':
                #self.default_coolingstpt = float(data["CoolingStPt"])
                #self.default_heatingstpt = float(data["HeatingStPt"])
                self.default_coolingstpt = 75.0
                self.default_heatingstpt = 65.0
                self.default_firststage_fanspeed = float(data["CoolSupplyFanSpeed1"])
                self.default_secondstage_fanspeed = float(data["CoolSupplyFanSpeed2"])
                self.default_damperstpt = float(data["ESMDamperMinPosition"])
                
            if self.state == 'STARTUP':
                self.state = 'IDLE'
                
        @matching.match_exact(topics.OPENADR_EVENT())
        def _on_dr_event(self, topic, headers, message, match):
            if self.state == 'STARTUP':
                print "DR event ignored because of startup."
                return
            """handle openADR events"""
            msg = jsonapi.loads(message[0])
            print('EVENT Received')
            print(msg)
            e_id = msg['id']
            e_status = msg['status']
            e_start = msg['start']
            e_start = datetime.datetime.strptime(e_start, datefmt)
            today = datetime.datetime.now().date()
            #e_start_day = e_start.date()
            #e_end = e_start.replace(hour=cpp_end_hour, minute =0, second = 0)
            current_datetime = datetime.datetime.now()
            e_end = e_start  + datetime.timedelta(minutes=2)
            
            if current_datetime > e_end:
                print 'Too Late Event is Over'
                return
            
            
            if e_status == 'cancelled':
                if e_start in self.all_scheduled_events:
                    print 'Event Cancelled'
                    self.all_scheduled_events[e_start].cancel()
                    del self.all_scheduled_events[e_start]
                    
                if e_start.date() == today and (self.state == 'PRECOOL' or self.state == 'DR_EVENT'):
                    self.cancel_event()
                return
                    
            #TODO: change this to UTC later
            #utc_now = datetime.datetime.utcnow()

            if today > e_start.date():
                if e_start in self.all_scheduled_events:
                    self.all_scheduled_events[e_start].cancel()
                    del self.all_scheduled_events[e_start]
                    
                return
            
            for item in self.all_scheduled_events.keys():
                if e_start.date() == item.date():
                    if e_start.time() != item.time():
                        print "Updating Event"
                        self.all_scheduled_events[item].cancel()
                        del self.all_scheduled_events[item]
                        if e_start.date() == today and (self.state == 'PRECOOL' or self.state == 'DR_EVENT'):
                            self.update_running_event()
                            self.state = 'IDLE'
                        break
                    elif e_start.time() == item.time():
                        print "same event"
                        return
            #if e_id in self.all_scheduled_dr_events and update is None:
#                 if e_id == self.currently_running_msg:
#                 return
                #return
                
            #Minutes used for testing   
            #event_start = e_start - datetime.timedelta(hours = max_precool_hours)  
            event_start = e_start - datetime.timedelta(minutes = max_precool_hours)
            
            event = sched.Event(self.pre_cool_get_lock, args=[e_start, e_end])
            self.schedule(event_start, event) 
            self.all_scheduled_events[e_start] = event                
                
        def pre_cool_get_lock(self, e_start,e_end):
            
            now = datetime.datetime.now()
            day=now.weekday()
          
            if not Schedule[day]:
                print"Unoccupied today"
                return
                
            self.state = 'PRECOOL'
            
            #e_end = e_start.replace(hour=cpp_end_hour, minute =0, second = 0)
            #e_end = e_start + datetime.timedelta(minutes=2)
            e_start_unix = time.mktime(e_start.timetuple())
            e_end_unix = time.mktime(e_end.timetuple())
           
            def run_schedule_builder():
                #current_time = time.mktime(current_time.timetuple())
                self.schedule_builder(e_start_unix, e_end_unix,
                                      current_spacetemp=77.0,
                                      pre_csp=csp_pre,
                                      building_thermal_constant=building_thermal_constant,
                                      normal_coolingstpt=76.0,
                                      timestep_length=timestep_length,
                                      dr_csp=csp_cpp)
                self.lock_handler=None
            
            self.lock_handler = run_schedule_builder
            
            headers = {
                headers_mod.CONTENT_TYPE: headers_mod.CONTENT_TYPE.JSON,
                'requesterID': agent_id}
            self.publish(topics.ACTUATOR_LOCK_ACQUIRE(**rtu_path), headers)
            
        def modify_temp_set_point(self, csp, hsp):
            self.publish(topics.ACTUATOR_SET(point='StandardDamperChangeOverSetPoint', **rtu_path), self.headers, str(csp))
            self.publish(topics.ACTUATOR_SET(point='StandardDamperMinPosition', **rtu_path), self.headers, str(hsp))
            
            def backup_run():
                self.modify_temp_set_point(csp, hsp)
                self.lock_handler=None
                
            self.lock_handler = backup_run
            
        def start_dr_event(self):
            self.state = 'DR_EVENT'
            self.publish(topics.ACTUATOR_SET(point='StandardDamperChangeOverSetPoint', **rtu_path), self.headers, str(csp_cpp))
            
            new_fan_speed = self.default_firststage_fanspeed - (self.default_firststage_fanspeed * fan_reduction)
            new_fan_speed = max(new_fan_speed,0)
            self.publish(topics.ACTUATOR_SET(point='CoolSupplyFanSpeed1', **rtu_path), self.headers, str(new_fan_speed))
            
            new_fan_speed = self.default_secondstage_fanspeed - (self.default_firststage_fanspeed * fan_reduction)
            new_fan_speed = max(new_fan_speed,0)
            self.publish(topics.ACTUATOR_SET(point='CoolSupplyFanSpeed2', **rtu_path), self.headers, str(new_fan_speed))            
            
            self.publish(topics.ACTUATOR_SET(point='ESMDamperMinPosition', **rtu_path), self.headers, str(damper_cpp))
                
            def backup_run():
                self.start_dr_event()
                self.lock_handler=None
                
            self.lock_handler = backup_run
            
        def start_restore_event(self, csp, hsp):
            self.state = 'RESTORE'
            print 'Restore:  Begin restoring normal operations'
            self.publish(topics.ACTUATOR_SET(point='StandardDamperChangeOverSetPoint', **rtu_path), self.headers, str(csp))
            self.publish(topics.ACTUATOR_SET(point='StandardDamperMinPosition', **rtu_path), self.headers, str(hsp)) #heating
            self.publish(topics.ACTUATOR_SET(point='CoolSupplyFanSpeed1', **rtu_path), self.headers, str(self.default_firststage_fanspeed))
            self.publish(topics.ACTUATOR_SET(point='CoolSupplyFanSpeed2', **rtu_path), self.headers, str(self.default_secondstage_fanspeed))            
            
            self.publish(topics.ACTUATOR_SET(point='ESMDamperMinPosition', **rtu_path), self.headers, str(self.default_damperstpt))
                
            def backup_run():
                self.start_restore_event(csp, hsp)
                self.lock_handler=None
                
            self.lock_handler = backup_run
            
        def update_running_event(self):
            self.publish(topics.ACTUATOR_SET(point='StandardDamperChangeOverSetPoint', **rtu_path), self.headers, str(self.default_coolingstpt))
            self.publish(topics.ACTUATOR_SET(point='StandardDamperMinPosition', **rtu_path), self.headers, str(self.default_heatingstpt))
            self.publish(topics.ACTUATOR_SET(point='CoolSupplyFanSpeed1', **rtu_path), self.headers, str(self.default_firststage_fanspeed))
            self.publish(topics.ACTUATOR_SET(point='CoolSupplyFanSpeed2', **rtu_path), self.headers, str(self.default_secondstage_fanspeed))            
            
            self.publish(topics.ACTUATOR_SET(point='ESMDamperMinPosition', **rtu_path), self.headers, str(self.default_damperstpt))
            
            for event in self.currently_running_dr_event_handlers:
                event.cancel()
            self.currently_running_dr_event_handlers = []
            
        def cancel_event(self):
            self.state = 'CLEANUP'
            self.publish(topics.ACTUATOR_SET(point='StandardDamperChangeOverSetPoint', **rtu_path), self.headers, str(self.default_coolingstpt))
            self.publish(topics.ACTUATOR_SET(point='StandardDamperMinPosition', **rtu_path), self.headers, str(self.default_heatingstpt))
            self.publish(topics.ACTUATOR_SET(point='CoolSupplyFanSpeed1', **rtu_path), self.headers, str(self.default_firststage_fanspeed))
            self.publish(topics.ACTUATOR_SET(point='CoolSupplyFanSpeed2', **rtu_path), self.headers, str(self.default_secondstage_fanspeed))            
            
            self.publish(topics.ACTUATOR_SET(point='ESMDamperMinPosition', **rtu_path), self.headers, str(self.default_damperstpt))
            
            for event in self.currently_running_dr_event_handlers:
                event.cancel()
                
            self.currently_running_dr_event_handlers = []
            def backup_run():
                self.cancel_event()
                self.lock_handler=None
                
            self.lock_handler = backup_run
            
            expected_values = {'StandardDamperChangeOverSetPoint': self.default_coolingstpt,
                               'StandardDamperMinPosition': self.default_heatingstpt,
                               'CoolSupplyFanSpeed1': self.default_firststage_fanspeed,
                               'CoolSupplyFanSpeed2': self.default_secondstage_fanspeed,
                               'ESMDamperMinPosition': self.default_damperstpt}
            
            EPSILON = 0.5  #allowed difference from expected value
            
            def result_handler(point, value):
                #print "actuator point being handled:", point, value
                expected_value = expected_values.pop(point, None)
                if expected_value is not None:
                    diff = abs(expected_value-value)
                    if diff > EPSILON:
                        _log.debug( "Did not get back expected value for", point)
                        
                if not expected_values:
                    self.actuator_handler = None
                    self.lock_handler=None
                    self.state = 'IDLE'
                    
                    headers = {
                        headers_mod.CONTENT_TYPE: headers_mod.CONTENT_TYPE.JSON,
                        'requesterID': agent_id}
                    self.publish(topics.ACTUATOR_LOCK_RELEASE(**rtu_path), headers)
            
            self.actuator_handler = result_handler
          
        def schedule_builder(self,start_time, end_time, 
                             current_spacetemp,
                             pre_csp,
                             building_thermal_constant,
                             normal_coolingstpt,
                             timestep_length,
                             dr_csp):
            """schedule all events for a DR event."""
           

            print 'Scheduling all DR actions'   
            pre_hsp = pre_csp - 5.0
            current_time = time.time()
            ideal_cooling_window = int(((current_spacetemp - pre_csp)/building_thermal_constant) *3600)  
            ideal_precool_start_time = start_time - ideal_cooling_window
            
            max_cooling_window = start_time - current_time
            
            cooling_window = ideal_cooling_window if ideal_cooling_window < max_cooling_window else max_cooling_window
            
            precool_start_time = start_time - cooling_window
     
            if (max_cooling_window > 0):
                print "Schedule Pre Cooling" 
                num_cooling_timesteps = int(math.ceil(float(cooling_window) / float(timestep_length)))         
                cooling_step_delta = (normal_coolingstpt - pre_csp) / num_cooling_timesteps
                
                for step_index in range (1, num_cooling_timesteps+1):
                    event_time = start_time - (step_index * timestep_length)
                    csp = pre_csp + ((step_index-1)*cooling_step_delta)
                    
                    print 'Precool step:', datetime.datetime.fromtimestamp(event_time), csp
                    event = sched.Event(self.modify_temp_set_point, args = [csp, pre_hsp])
                    self.schedule(event_time, event)
                    self.currently_running_dr_event_handlers.append(event)
            
            else:
                print "Too late to pre-cool!"
            
            restore_window = int(((dr_csp - normal_coolingstpt)/building_thermal_constant) *3600)  
            restore_start_time = end_time
            num_restore_timesteps = int(math.ceil(float(restore_window) / float(timestep_length)))         
            restore_step_delta = (dr_csp - normal_coolingstpt) / num_restore_timesteps
                
            print 'Schedule DR Event:', datetime.datetime.fromtimestamp(start_time), dr_csp
            event = sched.Event(self.start_dr_event)
            self.schedule(start_time, event)
            self.currently_running_dr_event_handlers.append(event)
            
            print 'Schedule Restore Event:', datetime.datetime.fromtimestamp(end_time), dr_csp-restore_step_delta
            event = sched.Event(self.start_restore_event, args = [dr_csp-restore_step_delta, self.default_heatingstpt])
            self.schedule(end_time, event)
            self.currently_running_dr_event_handlers.append(event)
                
            for step_index in range (1, num_restore_timesteps):
                event_time = end_time + (step_index * timestep_length)
                csp = dr_csp - ((step_index + 1) * restore_step_delta)
                
                print 'Restore step:', datetime.datetime.fromtimestamp(event_time), csp
                event = sched.Event(self.modify_temp_set_point, args = [csp, self.default_heatingstpt])
                self.schedule(event_time, event)
                self.currently_running_dr_event_handlers.append(event)
            
            event_time = end_time + (num_restore_timesteps * timestep_length)
            print 'Schedule Cleanup Event:', datetime.datetime.fromtimestamp(event_time)
            event = sched.Event(self.cancel_event)
            self.schedule(event_time,event)
            self.currently_running_dr_event_handlers.append(event)
               
    Agent.__name__ = 'DemandResponseAgent'
    return Agent(**kwargs) 
Esempio n. 8
0
def ArchiverAgent(config_path, **kwargs):
    config = utils.load_config(config_path)

    def get_config(name):
        try:
            value = kwargs.pop(name)
        except KeyError:
            return config[name]

    agent_id = get_config('agentid')
    source_name = get_config('source_name')
    archiver_url = get_config('archiver_url')

    class Agent(PublishMixin, BaseAgent):
        def __init__(self, **kwargs):
            super(Agent, self).__init__(**kwargs)

    #         self.subscribe(settings.REQUEST_TOPIC, self.handle_request)

        @match_start(topics.BASE_ARCHIVER_REQUEST)
        def handle_request(self, topic, headers, message, matched):
            # Path is part of topic.
            path = topic[len(topics.BASE_ARCHIVER_REQUEST):]

            # Range is message.  It will either be "start"-"end" or 1h, 1d,
            # etc... from now
            range_str = message[0]
            source = headers.get('SourceName', source_name)

            # Find UUID for path
            payload = ('select uuid where Metadata/SourceName="{}" '
                       'and Path="{}"'.format(source, path))

            done = False
            retries = 0
            while not done and retries <= 5:
                # TODO: Need to do some error handling here!
                try:
                    r = requests.post(archiver_url, data=payload)
                    if r.status_code == 200:
                        # Data should be a list of dictionaries at this point in time
                        uuid_list = jsonapi.loads(r.text)
                        done = True
                    else:
                        print str(retries) + ": " + str(
                            r.status_code) + ": " + payload
                        retries += 1
                except ValueError as e:
                    print str(retries) + ": " + str(e) + ": " + payload
                    retries += 1
                except ConnectionError as e:
                    print str(retries) + ": " + str(e) + ": " + payload
                    retries += 1
                    #Can get a 503 network busy
                    #TODO: Respond with error

            # TODO: Need to do some error handling here!

            # Data should be a list of dictionaries at this point in time
            if not uuid_list:
                # TODO: log this error
                return
            payload_template = "select data in {} where {{}}".format(range_str)
            if len(uuid_list) == 0:
                return

            uuid_clause = "uuid='{}'".format(uuid_list[0]['uuid'])
            for stream in uuid_list[1:]:
                uuid_clause.append(" or uuid='{}'".format(stream['uuid']))
            payload = payload_template.format(uuid_clause)

            full_data = None
            tries = 0
            done = False
            while not done and retries <= 5:
                # TODO: Need to do some error handling here!
                try:
                    r = requests.post(archiver_url, data=payload)
                    if 'Syntax error' in r.text:
                        # TODO Log this error
                        self.publish(topics.BASE_ARCHIVER_RESPONSE + path,
                                     None, 'Syntax error in date range')
                        return

            # Request data for UUID in range
            #[{"uuid": "5b94d5ed-1e1d-51cf-a6d8-afae5c055292", "Readings": [[1368750281000.0, 75.5], [1368750341000.0, 75.5], ...
                    done = True
                except ValueError as e:
                    print str(retries) + ": " + str(e) + ": " + payload
                    retries += 1
                except ConnectionError as e:
                    print str(retries) + ": " + str(e) + ": " + payload
                    retries += 1

            if 'Syntax error' in r.text:
                # TODO Log this error
                self.publish(topics.BASE_ARCHIVER_RESPONSE + path, None,
                             'Syntax error in date range')
                return

            # Request data for UUID in range
            #[{"uuid": "5b94d5ed-1e1d-51cf-a6d8-afae5c055292",
            # "Readings": [[1368750281000.0, 75.5], [1368750341000.0, 75.5], ...
            full_data = jsonapi.loads(r.text)
            data = full_data[0].get('Readings', [])
            pub_headers = {
                headers_mod.FROM:
                'ArchiverAgent',
                headers_mod.TO:
                headers[headers_mod.FROM]
                if headers_mod.FROM in headers else 'Unknown'
            }
            if data > 0:
                # There was data for this stream in the specified range.
                # Convert data to json and publish
                self.publish_json(topics.BASE_ARCHIVER_RESPONSE + path,
                                  pub_headers, data)

        @match_start(topics.BASE_ARCHIVER_FULL_REQUEST)
        def handle_full_request(self, topic, headers, message, matched):
            # Path is part of topic.
            path = topic[len(topics.BASE_ARCHIVER_FULL_REQUEST):]
            result_layout = headers.get('ResultLayout', 'HIERARCHICAL')

            # Range is message.  It will either be "start"-"end" or 1h, 1d,
            # etc... from now
            range_str = message[0]
            source = headers.get('SourceName', source_name)

            # Find UUID for path
            payload = ('select * where Metadata/SourceName="{}" '
                       'and Path~"{}"'.format(source, path))

            done = False
            retries = 0
            while not done and retries <= 5:
                # TODO: Need to do some error handling here!
                try:
                    r = requests.post(archiver_url, data=payload)
                    if r.status_code == 200:
                        # Data should be a list of dictionaries at this point in time
                        uuid_list = jsonapi.loads(r.text)
                        done = True
                    else:
                        print str(retries) + ": " + str(
                            r.status_code) + ": " + payload
                        retries += 1
                except ValueError as e:
                    print str(retries) + ": " + str(e) + ": " + payload
                    retries += 1
                except ConnectionError as e:
                    print str(retries) + ": " + str(e) + ": " + payload
                    retries += 1
                    #Can get a 503 network busy
                    #TODO: Respond with error

            # TODO: Need to do some error handling here!

            # Data should be a list of dictionaries at this point in time
            if not uuid_list:
                # TODO: log this error
                return

            timeseries = {}
            hierarchichal = {}

            payload_template = "select data in {} where {{}}".format(range_str)
            if len(uuid_list) == 0:
                return

            uuid_clause = "uuid='{}'".format(uuid_list[0]['uuid'])
            for stream in uuid_list[1:]:
                uuid_clause += (" or uuid='{}'".format(stream['uuid']))
            payload = payload_template.format(uuid_clause)
            #
            # Request data and store Readings in timeseries[path]
            full_data = None
            tries = 0
            done = False
            while not done and retries <= 5:
                # TODO: Need to do some error handling here!
                try:
                    r = requests.post(archiver_url, data=payload)
                    if 'Syntax error' in r.text:
                        # TODO Log this error
                        self.publish(topics.BASE_ARCHIVER_RESPONSE + path,
                                     None, 'Syntax error in date range')
                        return

            # Request data for UUID in range
            #[{"uuid": "5b94d5ed-1e1d-51cf-a6d8-afae5c055292", "Readings": [[1368750281000.0, 75.5], [1368750341000.0, 75.5], ...
                    done = True
                except ValueError as e:
                    print str(retries) + ": " + str(e) + ": " + payload
                    retries += 1
                except ConnectionError as e:
                    print str(retries) + ": " + str(e) + ": " + payload
                    retries += 1

            if 'Syntax error' in r.text:
                # TODO Log this error
                self.publish(topics.BASE_ARCHIVER_RESPONSE + path, None,
                             'Syntax error in date range')
                return

            # Request data for UUID in range
            #[{"uuid": "5b94d5ed-1e1d-51cf-a6d8-afae5c055292",
            # "Readings": [[1368750281000.0, 75.5], [1368750341000.0, 75.5], ...

            full_data = jsonapi.loads(r.text)

            reading_dict = {}

            for readings in full_data:
                reading_dict[readings['uuid']] = readings.get('Readings', [])

            for stream in uuid_list:
                #                 uuid_clause += (" or uuid='{}'".format(stream['uuid']))
                path = stream['Path']
                print stream
                (hierarchichal,
                 timeseries) = build_paths(path, hierarchichal, timeseries)
                timeseries[path]['uuid'] = stream['uuid']
                timeseries[path]['Properties'] = stream['Properties']
                timeseries[path]['Path'] = path
                timeseries[path]['Readings'] = reading_dict[stream['uuid']]

            pub_headers = {
                headers_mod.FROM:
                'ArchiverAgent',
                headers_mod.TO:
                headers[headers_mod.FROM]
                if headers_mod.FROM in headers else 'Unknown'
            }

            if result_layout == 'FLAT':
                self.publish_json(topics.BASE_ARCHIVER_RESPONSE + path,
                                  pub_headers, timeseries)
            else:
                self.publish_json(topics.BASE_ARCHIVER_RESPONSE + path,
                                  pub_headers, hierarchichal)

    Agent.__name__ = 'ArchiverAgent'
    return Agent(**kwargs)
Esempio n. 9
0
def SMDSAgent(config_path, **kwargs):
    config = utils.load_config(config_path)

    def get_config(name):
        try:
            value = kwargs.pop(name)
        except KeyError:
            return config[name]

    agent_id = get_config('agentid')
    time_window_minutes = get_config('time_window_minutes')
    rtu_path = {
        'campus': get_config('campus'),
        'building': get_config('building'),
        'unit': get_config('unit'),
    }

    class Agent(PublishMixin, BaseAgent):
        '''This agent grabs a day's worth of data for a Catalyst's Data points
        out of the historian. It then sends the data on to an application
        in the cloud.
        '''
        def __init__(self, **kwargs):
            super(Agent, self).__init__(**kwargs)
            self._raw_air_temp = None
            self._raw_fan_speed = None
            self._raw_unit_power = None

        def setup(self):
            self._agent_id = get_config('agentid')
            self._service_url = get_config('service_url')
            self._provider_id = get_config('provider_id')
            self._unit_power_chan = get_config('unit_power_chan')
            self._outdoor_temp_chan = get_config('outdoor_temp_chan')
            self._fan_supply_chan = get_config('fan_supply_chan')
            self._campusid = get_config('campus')
            self._buildingid = get_config('building')
            self._deviceid = get_config('unit')

            #             self._time_window_minutes = int(self.config['time_window_minutes'])
            self._backlog_hours = get_config('backlog_hours')

            # Always call the base class setup()
            super(Agent, self).setup()

            self.setup_topics()

            self._catching_up = True

            self._last_update = datetime.now() - timedelta(
                hours=self._backlog_hours)
            self._query_end_time = None

            self.publish_requests()

        def setup_topics(self):

            self.request_temptopic = topics.ARCHIVER_REQUEST(
                point='OutsideAirTemperature', **rtu_path)
            self.request_powertopic = topics.ARCHIVER_REQUEST(
                point='UnitPower', **rtu_path)
            self.request_fantopic = topics.ARCHIVER_REQUEST(
                point='SupplyFanSpeed', **rtu_path)

        @matching.match_headers({headers_mod.TO: agent_id})
        @matching.match_exact(
            topics.ARCHIVER_RESPONSE(point='OutsideAirTemperature',
                                     **rtu_path))
        def on_temp_response(self, topic, headers, message, match):
            '''Method for dealing with temp data from smap'''
            if DEBUG:
                print "Topic: {topic}, Headers: {headers}, Message: {message}".format(
                    topic=topic, headers=headers, message=message)

            self._raw_air_temp = message[0]
            self.go_if_ready()

        @matching.match_exact(
            topics.ARCHIVER_RESPONSE(point='UnitPower', **rtu_path))
        @matching.match_headers({headers_mod.TO: agent_id})
        def on_unit_power(self, topic, headers, message, match):
            '''Method for dealing with power data from smap'''
            if DEBUG:
                print "Topic: {topic}, Headers: {headers}, Message: {message}".format(
                    topic=topic, headers=headers, message=message)
            self._raw_unit_power = message[0]
            self.go_if_ready()

        @matching.match_headers({headers_mod.TO: agent_id})
        @matching.match_exact(
            topics.ARCHIVER_RESPONSE(point='SupplyFanSpeed', **rtu_path))
        def on_fan_speed(self, topic, headers, message, match):
            '''Method for dealing with fan data from smap'''
            if DEBUG:
                print "Topic: {topic}, Headers: {headers}, Message: {message}".format(
                    topic=topic, headers=headers, message=message)
            self._raw_fan_speed = message[0]
            self.go_if_ready()

        def go_if_ready(self):
            if (self._raw_air_temp != None and self._raw_fan_speed != None
                    and self._raw_unit_power != None):
                message = self.convert_raw()
                worked = self.post_data(message)
                if (worked):
                    self._raw_air_temp = None
                    self._raw_fan_speed = None
                    self._raw_unit_power = None
                    self._last_update = self._query_end_time
                    if self._catching_up:
                        #                     self.publish_requests
                        self.timer(1, self.publish_requests)

        def make_dataset(self, message, channelid, units):
            list = eval(message)
            values = []
            if DEBUG:
                print len(list)

            if len(list) >= 1:

                start_time = list[0][0]

                time_index = start_time

                for data in list:
                    values.append({
                        "Utc": "/Date({})/".format(str(int(data[0]))),
                        "Val": data[1]
                    })

            return {"ChannelId": channelid, "Units": units, "Values": values}

        def convert_raw(self):
            dataset = []

            dataset.append(
                self.make_dataset(
                    self._raw_air_temp,
                    self._provider_id + "/" + self._outdoor_temp_chan,
                    "DegreesF"))

            dataset.append(
                self.make_dataset(
                    self._raw_fan_speed,
                    self._provider_id + "/" + self._fan_supply_chan, "%"))

            dataset.append(
                self.make_dataset(
                    self._raw_unit_power,
                    self._provider_id + "/" + self._unit_power_chan, "kW"))

            providerid = self._provider_id
            reply = {"ProviderId": providerid, "Datalogs": dataset}

            #         reply = json.dumps(reply).replace('/','\/')
            if DEBUG:
                print json.dumps(reply,
                                 sort_keys=True,
                                 indent=4,
                                 separators=(',', ': '))

            return reply

        # Periodically get data and push to cloud service
        @periodic(time_window_minutes * 60)
        def publish_requests(self):
            '''Publish lookup requests to the ArchiverAgent
            '''

            now = datetime.now()

            if (now - self._last_update) > timedelta(
                    minutes=time_window_minutes):
                self._catching_up = True
                self._query_end_time = self._last_update + timedelta(
                    minutes=time_window_minutes)
            else:
                self._catching_up = False
                self._query_end_time = now

    #             if DEBUG:
    #Print a readable time range
            start = self._last_update.strftime(readable_format)
            end = (self._query_end_time).strftime(readable_format)
            print '({start}, {end})'.format(start=start, end=end)

            start = self._last_update.strftime(date_format)
            end = (self._query_end_time).strftime(date_format)

            if DEBUG:
                print '({start}, {end})'.format(start=start, end=end)

            headers = {
                headers_mod.FROM: agent_id,
                headers_mod.TO: 'ArchiverAgent'
            }

            self.publish(self.request_temptopic, headers,
                         '({start}, {end})'.format(start=start, end=end))
            self.publish(self.request_powertopic, headers,
                         '({start}, {end})'.format(start=start, end=end))
            self.publish(self.request_fantopic, headers,
                         '({start}, {end})'.format(start=start, end=end))

        def post_data(self, params):

            post_data = json.dumps(params)

            headers_post = {
                'Content-Type': 'application/json',
                'User-Agent': 'RTUNetwork',
                'Accept': 'application/json',
                'Connection': 'close'
            }
            done = False
            tries = 0
            while (done != True and tries < 5):
                try:
                    response = requests.post(self._service_url,
                                             data=post_data,
                                             headers=headers_post)
                    done = True
                except ConnectionError as e:
                    print '{}: {}: {}'.format(str(tries), str(e), post_data)
                    tries += 1

            worked = False

            if (response.content != None and response.content != ""):

                root = ET.fromstring(response.content)

                is_error = root.find(
                    '{http://schemas.datacontract.org/2004/07/RestServiceWebRoles}IsError'
                ).text
                transaction_id = root.find(
                    '{http://schemas.datacontract.org/2004/07/RestServiceWebRoles}TransactionId'
                ).text

                worked = is_error.lower(
                ) == 'false' and int(transaction_id) > 0
            else:
                worked = False

            return worked

    Agent.__name__ = 'SMDSAgent'
    return Agent(**kwargs)
Esempio n. 10
0
def ThermostatAgent(config_path, **kwargs):
    config = utils.load_config(config_path)

    def get_config(name):
        try:
            kwargs.pop(name)
        except KeyError:
            return config.get(name, '')

    # 1. @params agent
    agent_id = get_config('agent_id')
    LOG_DATA_PERIOD = get_config('poll_time')
    device_monitor_time = get_config('device_monitor_time')
    publish_address = 'ipc:///tmp/volttron-lite-agent-publish'
    subscribe_address = 'ipc:///tmp/volttron-lite-agent-subscribe'
    debug_agent = False
    agentknowledge = dict(day=["day"], hour=["hour"], minute=["minute"], temperature=["temp", "temperature",
                      "current_temp"], thermostat_mode=["tmode", "ther_mode", "thermostat_mode"],
                      fan_mode=["fmode", "fan_mode"], heat_setpoint=["t_heat", "temp_heat", "heat_setpoint"],
                      cool_setpoint=["t_cool", "temp_cool", "cool_setpoint"], thermostat_state=["tstate",
                      "thermostat_state"], fan_state=["fstate", "fan_state"])
    agentAPImapping = dict(temperature=[], thermostat_mode=[], fan_mode=[], heat_setpoint=[],
                           cool_setpoint=[], thermostat_state=[], fan_state=[])

    # 2. @params device_info
    building_name = get_config('building_name')
    zone_id = get_config('zone_id')
    # room = get_config('room')
    model = get_config('model')
    device_type = get_config('type')
    address = get_config('address')
    _address = address
    _address = _address.replace('http://', '')
    _address = _address.replace('https://', '')
    try:  # validate whether or not address is an ip address
        socket.inet_aton(_address)
        ip_address = _address
        # print "yes ip_address is {}".format(ip_address)
    except socket.error:
        # print "yes ip_address is None"
        ip_address = None
    identifiable = get_config('identifiable')
    # mac_address = get_config('mac_address')

    # 3. @params agent & DB interfaces
    # get database parameters from settings.py, add db_table for specific table
    db_host = get_config('db_host')
    db_port = get_config('db_port')
    db_database = get_config('db_database')
    db_user = get_config('db_user')
    db_password = get_config('db_password')
    db_table_thermostat = settings.DATABASES['default']['TABLE_thermostat']
    db_table_notification_event = settings.DATABASES['default']['TABLE_notification_event']

    # construct _topic_Agent_UI based on data obtained from DB
    _topic_Agent_UI = building_name+'/'+str(zone_id)+'/'+device_type+'/'+agent_id + '/'
    # print(_topic_Agent_UI)

    # construct _topic_Agent_sMAP based on data obtained from DB
    _topic_Agent_sMAP = 'datalogger/log/'+building_name+'/'+str(zone_id)+'/'+device_type+'/'+agent_id
    # print(_topic_Agent_sMAP)

    # 4. @params device_api
    api = get_config('api')
    # discovery agent locate the location of api in launch file e.g. "api": "testAPI.classAPI_RadioThermostat",
    apiLib = importlib.import_module("testAPI."+api)
    # print("testAPI."+api)

    # 4.1 initialize thermostat device object
    Thermostat = apiLib.API(model=model, device_type=device_type, api=api, address=address, agent_id=agent_id)

    print("{0}agent is initialized for {1} using API={2} at {3}".format(agent_id, Thermostat.get_variable('model'),
                                                                        Thermostat.get_variable('api'),
                                                                        Thermostat.get_variable('address')))

    # 5. @params notification_info
    send_notification = True
    email_fromaddr = settings.NOTIFICATION['email']['fromaddr']
    email_recipients = settings.NOTIFICATION['email']['recipients']
    email_username = settings.NOTIFICATION['email']['username']
    email_password = settings.NOTIFICATION['email']['password']
    email_mailServer = settings.NOTIFICATION['email']['mailServer']
    notify_heartbeat = settings.NOTIFICATION['heartbeat']

    class Agent(PublishMixin, BaseAgent):

        # 1. agent initialization
        def __init__(self, **kwargs):
            super(Agent, self).__init__(**kwargs)
            #1. initialize all agent variables
            self.variables = kwargs
            self.valid_data = False
            self._keep_alive = True
            self.first_time_update = True
            self.topic = _topic_Agent_sMAP
            self.ip_address = ip_address if ip_address != None else None
            self.flag = 1
            self.authorized_thermostat_mode = None
            self.authorized_fan_mode = None
            self.authorized_heat_setpoint = None
            self.authorized_cool_setpoint = None
            self.time_sent_notifications_device_tampering = datetime.datetime.now()
            self.first_time_detect_device_tampering = True
            self.event_ids = list()
            self.time_sent_notifications = {}
            self.notify_heartbeat = notify_heartbeat
            self._override = False
            #2. setup connection with db -> Connect to bemossdb database
            try:
                self.con = psycopg2.connect(host=db_host, port=db_port, database=db_database, user=db_user,
                                            password=db_password)
                self.cur = self.con.cursor()  # open a cursor to perform database operations
                print("{} connects to the database name {} successfully".format(agent_id, db_database))
            except:
                print("ERROR: {} fails to connect to the database name {}".format(agent_id, db_database))
            #3. send notification to notify building admin
            self.send_notification = send_notification
            # self.time_send_notification = 0
            # if self.send_notification:
            self.subject = 'Message from ' + agent_id
            #     self.text = 'Now an agent device_type {} for {} with API {} at address {} is launched!'.format(
            #         Thermostat.get_variable('device_type'), Thermostat.get_variable('model'),
            #         Thermostat.get_variable('api'), Thermostat.get_variable('address'))
            #     emailService = EmailService()
            #     emailService.sendEmail(email_fromaddr, email_recipients, email_username, email_password, self.subject,
            #                            self.text, email_mailServer)

        # These set and get methods allow scalability
        def set_variable(self, k, v):  # k=key, v=value
            self.variables[k] = v
    
        def get_variable(self, k):
            return self.variables.get(k, None)  # default of get_variable is none
        
        # 2. agent setup method
        def setup(self):
            super(Agent, self).setup()
            #1. Do a one time push when we start up so we don't have to wait for the periodic
            self.timer(1, self.deviceMonitorBehavior)
            if identifiable == "True": Thermostat.identifyDevice()
            try:
                # update initial value of override column of a thermostat to False
                self.cur.execute("UPDATE "+db_table_thermostat+" SET override=%s WHERE thermostat_id=%s",
                                 (self._override, agent_id))
                self.con.commit()
            except:
                print "{} >> cannot update override column of thermostat".format(agent_id)

        # 3. deviceMonitorBehavior (CyclicBehavior)
        @periodic(device_monitor_time)
        def deviceMonitorBehavior(self):
            # step1: get current status of a thermostat, then map keywords and variables to agent knowledge
            try:
                Thermostat.getDeviceStatus()
                # mapping variables from API to Agent's knowledge
                for APIKeyword, APIvariable in Thermostat.variables.items():
                    if debug_agent: print (APIKeyword, APIvariable)
                    self.set_variable(self.getKeyword(APIKeyword), APIvariable)  # set variables of agent from API variables
                    agentAPImapping[self.getKeyword(APIKeyword)] = APIKeyword  # map keyword of agent and API
            except:
                print("device connection is not successful")

            if self.first_time_update:
                if self.get_variable('heat_setpoint') is None: self.set_variable('heat_setpoint', 70)
                else: pass
                if self.get_variable('cool_setpoint') is None: self.set_variable('cool_setpoint', 70)
                else: pass
                self.first_time_update = False
            else:
                pass

            # step2: send notification to a user if required
            if self.send_notification:
                self.track_event_send_notification()

            # step3: update PostgresQL (meta-data) database
            try:
                self.cur.execute("UPDATE "+db_table_thermostat+" SET temperature=%s WHERE thermostat_id=%s",
                                 (self.get_variable('temperature'), agent_id))
                self.con.commit()
                self.cur.execute("UPDATE "+db_table_thermostat+" SET fan_mode=%s WHERE thermostat_id=%s",
                                 (self.get_variable('fan_mode'), agent_id))
                self.con.commit()
                if self.get_variable('battery') is not None:
                    self.cur.execute("UPDATE "+db_table_thermostat+" SET battery=%s WHERE thermostat_id=%s",
                                 (self.get_variable('battery'), agent_id))
                    self.con.commit()
                if self.get_variable('thermostat_mode') == "HEAT":
                    self.cur.execute("UPDATE "+db_table_thermostat+" SET heat_setpoint=%s WHERE thermostat_id=%s",
                                     (self.get_variable('heat_setpoint'), agent_id))
                    self.con.commit()
                    self.cur.execute("UPDATE "+db_table_thermostat+" SET thermostat_mode=%s WHERE thermostat_id=%s",
                                     ('HEAT', agent_id))
                    self.con.commit()
                elif self.get_variable('thermostat_mode') == "COOL":
                    self.cur.execute("UPDATE "+db_table_thermostat+" SET cool_setpoint=%s WHERE thermostat_id=%s",
                                     (self.get_variable('cool_setpoint'), agent_id))
                    self.con.commit()
                    self.cur.execute("UPDATE "+db_table_thermostat+" SET thermostat_mode=%s WHERE thermostat_id=%s",
                                     ('COOL', agent_id))
                    self.con.commit()
                elif self.get_variable('thermostat_mode') == "OFF":
                    self.cur.execute("UPDATE "+db_table_thermostat+" SET thermostat_mode=%s WHERE thermostat_id=%s",
                                     ('OFF', agent_id))
                    self.con.commit()
                elif self.get_variable('thermostat_mode') == "AUTO":
                    self.cur.execute("UPDATE "+db_table_thermostat+" SET thermostat_mode=%s WHERE thermostat_id=%s",
                                     ('AUTO', agent_id))
                    self.con.commit()
                else:
                    pass

                if self.ip_address != None:
                    psycopg2.extras.register_inet()
                    _ip_address = psycopg2.extras.Inet(self.ip_address)
                    self.cur.execute("UPDATE "+db_table_thermostat+" SET ip_address=%s WHERE thermostat_id=%s",
                                     (_ip_address, agent_id))
                    self.con.commit()
                _time_stamp_last_scanned = str(datetime.datetime.now())
                self.cur.execute("UPDATE "+db_table_thermostat+" SET last_scanned_time=%s "
                                 "WHERE thermostat_id=%s",
                                 (_time_stamp_last_scanned, agent_id))
                self.con.commit()
                print("{} updates database name {} during deviceMonitorBehavior successfully".format(agent_id,db_database))
            except:
                print("ERROR: {} failed to update database name {}".format(agent_id, db_database))

            # step4: update sMAP (time-series) database
            try:
                self.publish_logdata1()
                self.publish_logdata2()
                self.publish_logdata3()
                self.publish_logdata4()
                self.publish_logdata5()
                self.publish_logdata6()
                self.publish_logdata7()
                print "{} success update sMAP database\n".format(agent_id)
            except:
                print("ERROR: {} fails to update sMAP database".format(agent_id))

            # step5: debug agent knowledge
            if debug_agent:
                print("printing agent's knowledge")
                for k, v in self.variables.items():
                    print (k, v)
                print('')

            if debug_agent:
                print("printing agentAPImapping's fields")
                for k, v in agentAPImapping.items():
                    if k is None:
                        agentAPImapping.update({v: v})
                        agentAPImapping.pop(k)
                for k, v in agentAPImapping.items():
                    print (k, v)
            
        def getKeyword(self, APIKeyword):
            for k, v in agentknowledge.items():
                if APIKeyword in agentknowledge[k]:
                    return k
                    flag = 1
                    break
                else:
                    flag = 0
                    pass
            if flag == 0:  # if flag still 0 means that a APIKeyword is not in an agent knowledge,
                           # then add it to agent knowledge
                return APIKeyword
        
        # 4. updateUIBehavior (generic behavior)
        @matching.match_exact('/ui/agent/'+_topic_Agent_UI+'device_status')
        def updateUIBehavior(self, topic, headers, message, match):
            print agent_id + " got\nTopic: {topic}".format(topic=topic)
            print "Headers: {headers}".format(headers=headers)
            print "Message: {message}\n".format(message=message)
            #reply message
            topic = '/agent/ui/'+_topic_Agent_UI+'device_status/response'
            # now = datetime.utcnow().isoformat(' ') + 'Z'
            headers = {
                'AgentID': agent_id,
                headers_mod.CONTENT_TYPE: headers_mod.CONTENT_TYPE.JSON,
                # headers_mod.DATE: now,
                headers_mod.FROM: agent_id,
                headers_mod.TO: 'ui'
            }
            #TODO add battery field to _data
            if self.get_variable('battery') != None:
                _data = {'temperature': self.get_variable('temperature'), 'thermostat_mode':
                         self.get_variable('thermostat_mode'), 'fan_mode': self.get_variable('fan_mode'),
                         'heat_setpoint': self.get_variable('heat_setpoint'), 'cool_setpoint': self.get_variable('cool_setpoint'),
                         'thermostat_state': self.get_variable('thermostat_state'), 'fan_state': self.get_variable('fan_state'),
                         'battery': self.get_variable('battery'), 'override': self._override
                }
            else:
                _data = {'temperature': self.get_variable('temperature'), 'thermostat_mode':
                         self.get_variable('thermostat_mode'), 'fan_mode': self.get_variable('fan_mode'),
                         'heat_setpoint': self.get_variable('heat_setpoint'), 'cool_setpoint': self.get_variable('cool_setpoint'),
                         'thermostat_state': self.get_variable('thermostat_state'), 'fan_state': self.get_variable('fan_state'),
                         'override': self._override
                }
            message = json.dumps(_data) 
            message = message.encode(encoding='utf_8')
            self.publish(topic, headers, message)

        # 5. deviceControlBehavior (generic behavior)
        @matching.match_exact('/ui/agent/'+_topic_Agent_UI+'update')
        def deviceControlBehavior(self, topic, headers, message, match):
            print agent_id + " got\nTopic: {topic}".format(topic=topic)
            print "Headers: {headers}".format(headers=headers)
            print "Message: {message}\n".format(message=message)
            #step1: change device status according to the receive message
            if self.isPostmsgValid(message[0]):  # check if the data is valid
                # _data = json.dumps(message[0])
                _data = json.loads(message[0])
                for k, v in _data.items():
                    if k == 'thermostat_mode':
                        self.authorized_thermostat_mode = _data.get('thermostat_mode')
                        if _data.get('thermostat_mode') == "HEAT":
                            for k, v in _data.items():
                                if k == 'heat_setpoint': self.authorized_heat_setpoint = _data.get('heat_setpoint')
                                else: pass
                        elif _data.get('thermostat_mode') == "COOL":
                            for k, v in _data.items():
                                if k == 'cool_setpoint': self.authorized_cool_setpoint = _data.get('cool_setpoint')
                                else: pass
                    elif k == 'fan_mode':
                        self.authorized_fan_mode = _data.get('fan_mode')
                    else: pass
                print "{} >> self.authorized_thermostat_mode {}".format(agent_id, self.authorized_thermostat_mode)
                print "{} >> self.authorized_heat_setpoint {}".format(agent_id, self.authorized_heat_setpoint)
                print "{} >> self.authorized_cool_setpoint {}".format(agent_id, self.authorized_cool_setpoint)
                print "{} >> self.authorized_fan_mode {}".format(agent_id, self.authorized_fan_mode)
                setDeviceStatusResult = Thermostat.setDeviceStatus(json.loads(message[0]))  # convert received message from string to JSON
                #TODO need to do additional checking whether the device setting is actually success!!!!!!!!
                #step2: update agent's knowledge on this device
                Thermostat.getDeviceStatus()
                #step3: send reply message back to the UI
                topic = '/agent/ui/'+_topic_Agent_UI+'update/response'
                # now = datetime.utcnow().isoformat(' ') + 'Z'
                headers = {
                    'AgentID': agent_id,
                    headers_mod.CONTENT_TYPE: headers_mod.CONTENT_TYPE.PLAIN_TEXT,
                    # headers_mod.DATE: now,
                }
                if setDeviceStatusResult:
                    message = 'success'
                else:
                    message = 'failure'
            else:
                print("The POST message is invalid, check thermostat_mode, heat_setpoint, cool_setpoint "
                      "setting and try again\n")
                message = 'failure'
            self.publish(topic, headers, message)

        def isPostmsgValid(self, postmsg):  # check validity of postmsg
            dataValidity = True
            try:
                # _data = json.dumps(postmsg)
                _data = json.loads(postmsg)
                for k, v in _data.items():
                    if k == 'thermostat_mode':
                        self.authorized_thermostat_mode = _data.get('thermostat_mode')
                        if _data.get('thermostat_mode') == "HEAT":
                            for k, v in _data.items():
                                if k == 'heat_setpoint': self.authorized_heat_setpoint = _data.get('heat_setpoint')
                                elif k == 'cool_setpoint':
                                    dataValidity = False
                                    break
                                else: pass
                        elif _data.get('thermostat_mode') == "COOL":
                            for k, v in _data.items():
                                if k == 'cool_setpoint': self.authorized_cool_setpoint = _data.get('cool_setpoint')
                                elif k == 'heat_setpoint':
                                    dataValidity = False
                                    break
                                else: pass
                    elif k == 'fan_mode':
                        self.authorized_fan_mode = _data.get('fan_mode')
                    else: pass
            except:
                dataValidity = True
                print("dataValidity failed to validate data comes from UI")
            return dataValidity

        # 6. deviceIdentifyBehavior (generic behavior)
        @matching.match_exact('/ui/agent/'+_topic_Agent_UI+'identify')
        def deviceIdentifyBehavior(self, topic, headers, message, match):
            print agent_id+ " got\nTopic: {topic}".format(topic=topic)
            print "Headers: {headers}".format(headers=headers)
            print "Message: {message}\n".format(message=message)
            #step1: change device status according to the receive message
            identifyDeviceResult = Thermostat.identifyDevice()
            #TODO need to do additional checking whether the device setting is actually success!!!!!!!!
            #step2: send reply message back to the UI
            topic = '/agent/ui/'+_topic_Agent_UI+'identify/response'
            # now = datetime.utcnow().isoformat(' ') + 'Z'
            headers = {
                'AgentID': agent_id,
                headers_mod.CONTENT_TYPE: headers_mod.CONTENT_TYPE.PLAIN_TEXT,
                # headers_mod.DATE: now,
            }
            if identifyDeviceResult:
                message = 'success'
            else:
                message = 'failure'
            self.publish(topic, headers, message)
            
        # Filter agent knowledge before sending out data to sMAP
        def publish_logdata1(self):
            headers = {
                headers_mod.FROM: agent_id,
                headers_mod.CONTENT_TYPE: headers_mod.CONTENT_TYPE.JSON,
            }
            mytime = int(time.time())
            content = {
                "temperature": {
                    "Readings": [[mytime, float(self.get_variable("temperature"))]],
                    "Units": "F",
                    "data_type": "double"
                }
            }
            print("{} published temperature to an IEB".format(agent_id))
            self.publish(_topic_Agent_sMAP, headers, json.dumps(content))

        def publish_logdata2(self):
            headers = {
                headers_mod.FROM: agent_id,
                headers_mod.CONTENT_TYPE: headers_mod.CONTENT_TYPE.JSON,
            }
            mytime = int(time.time())
            if self.get_variable("thermostat_mode") == "OFF":
                _thermostat_mode = 0
            elif self.get_variable("thermostat_mode") == "HEAT":
                _thermostat_mode = 1
            elif self.get_variable("thermostat_mode") == "COOL":
                _thermostat_mode = 2
            elif self.get_variable("thermostat_mode") == "AUTO":
                _thermostat_mode = 3
            else:
                _thermostat_mode = 4
            content = {
                "thermostat_mode": {
                    "Readings": [[mytime, float(_thermostat_mode)]],
                    "Units": "N/A",
                    "data_type": "double"
                }
            }
            print("{} published thermostat_mode to an IEB".format(agent_id))
            self.publish(_topic_Agent_sMAP, headers, json.dumps(content))

        def publish_logdata3(self):
            headers = {
                headers_mod.FROM: agent_id,
                headers_mod.CONTENT_TYPE: headers_mod.CONTENT_TYPE.JSON,
            }
            mytime = int(time.time())
            if self.get_variable("fan_mode") == "AUTO":
                _fan_mode = 0
            elif self.get_variable("fan_mode") == "CIRCULATE":
                _fan_mode = 1
            elif self.get_variable("fan_mode") == "ON":
                _fan_mode = 2
            else:
                _fan_mode = 3
            content = {
                "fan_mode": {
                    "Readings": [[mytime, float(_fan_mode)]],
                    "Units": "N/A",
                    "data_type": "double"
                }
            }
            print("{} published fan_mode to an IEB".format(agent_id))
            self.publish(_topic_Agent_sMAP, headers, json.dumps(content))

        def publish_logdata4(self):
            headers = {
                headers_mod.FROM: agent_id,
                headers_mod.CONTENT_TYPE: headers_mod.CONTENT_TYPE.JSON,
            }
            mytime = int(time.time())
            content = {
                "heat_setpoint": {
                    "Readings": [[mytime, float(self.get_variable("heat_setpoint"))]],
                    "Units": "F",
                    "data_type": "double"
                }
            }
            print("{} published heat_setpoint to an IEB".format(agent_id))
            self.publish(_topic_Agent_sMAP, headers, json.dumps(content))

        def publish_logdata5(self):
            headers = {
                headers_mod.FROM: agent_id,
                headers_mod.CONTENT_TYPE: headers_mod.CONTENT_TYPE.JSON,
            }
            mytime = int(time.time())
            content = {
                "cool_setpoint": {
                    "Readings": [[mytime, float(self.get_variable("cool_setpoint"))]],
                    "Units": "F",
                    "data_type": "double"
                }
            }
            print("{} published cool_setpoint to an IEB".format(agent_id))
            self.publish(_topic_Agent_sMAP, headers, json.dumps(content))

        def publish_logdata6(self):
            headers = {
                headers_mod.FROM: agent_id,
                headers_mod.CONTENT_TYPE: headers_mod.CONTENT_TYPE.JSON,
            }
            mytime = int(time.time())
            if self.get_variable("thermostat_state") == "OFF":
                _thermostat_state = 0
            elif self.get_variable("thermostat_state") == "HEAT":
                _thermostat_state = 1
            elif self.get_variable("thermostat_state") == "COOL":
                _thermostat_state = 2
            else:
                _thermostat_state = 3
            content = {
                "thermostat_state": {
                    "Readings": [[mytime, float(_thermostat_state)]],
                    "Units": "N/A",
                    "data_type": "double"
                }
            }
            print("{} published thermostat_state to an IEB".format(agent_id))
            self.publish(_topic_Agent_sMAP, headers, json.dumps(content))

        def publish_logdata7(self):
            headers = {
                headers_mod.FROM: agent_id,
                headers_mod.CONTENT_TYPE: headers_mod.CONTENT_TYPE.JSON,
            }
            mytime = int(time.time())
            if self.get_variable("fan_state") == "OFF":
                _fan_state = 0
            elif self.get_variable("fan_state") == "ON":
                _fan_state = 1
            else:
                _fan_state = 2
            content = {
                "fan_state": {
                    "Readings": [[mytime, float(_fan_state)]],
                    "Units": "N/A",
                    "data_type": "double"
                }
            }
            print("{} published fan_state to an IEB".format(agent_id))
            self.publish(_topic_Agent_sMAP, headers, json.dumps(content))

        @matching.match_exact('/ui/agent/'+_topic_Agent_UI+'add_notification_event')
        def add_notification_event(self, topic, headers, message, match):
            print agent_id + " got\nTopic: {topic}".format(topic=topic)
            print "Headers: {headers}".format(headers=headers)
            print "Message: {message}".format(message=message)
            #reply message
            topic = '/agent/ui/'+_topic_Agent_UI+'add_notification_event/response'
            # now = datetime.utcnow().isoformat(' ') + 'Z'
            headers = {
                'AgentID': agent_id,
                headers_mod.CONTENT_TYPE: headers_mod.CONTENT_TYPE.JSON,
                # headers_mod.DATE: now,
                headers_mod.FROM: agent_id,
                headers_mod.TO: 'ui'
            }
            #add event_id to self.event_ids
            _data = json.loads(message[0])
            event_id = _data['event_id']
            print "{} added notification event_id: {}".format(agent_id, event_id)
            self.event_ids.append(event_id)
            _data = "success"
            message = _data
            # message = json.dumps(_data)
            # message = message.encode(encoding='utf_8')
            self.publish(topic, headers, message)

        @matching.match_exact('/ui/agent/'+_topic_Agent_UI+'remove_notification_event')
        def remove_notification_event(self, topic, headers, message, match):
            print agent_id + " got\nTopic: {topic}".format(topic=topic)
            print "Headers: {headers}".format(headers=headers)
            print "Message: {message}".format(message=message)
            #reply message
            topic = '/agent/ui/'+_topic_Agent_UI+'remove_notification_event/response'
            # now = datetime.utcnow().isoformat(' ') + 'Z'
            headers = {
                'AgentID': agent_id,
                headers_mod.CONTENT_TYPE: headers_mod.CONTENT_TYPE.JSON,
                # headers_mod.DATE: now,
                headers_mod.FROM: agent_id,
                headers_mod.TO: 'ui'
            }
            #add event_id to self.event_ids
            _data = json.loads(message[0])
            event_id = _data['event_id']
            print "{} removed notification event_id: {}".format(agent_id, event_id)
            self.event_ids.remove(event_id)
            _data = "success"
            message = _data
            # message = json.dumps(_data)
            # message = message.encode(encoding='utf_8')
            self.publish(topic, headers, message)

        def track_event_send_notification(self):
            for event_id in self.event_ids:
                print "{} is monitoring event_id: {}\n".format(agent_id, event_id)
                # collect information about event from notification_event table
                self.cur.execute("SELECT event_name, notify_device_id, triggered_parameter, comparator,"
                                 "threshold, notify_channel, notify_address, notify_heartbeat  FROM "
                                 + db_table_notification_event + " WHERE event_id=%s", (event_id,))
                if self.cur.rowcount != 0:
                    row = self.cur.fetchone()
                    event_name = str(row[0])
                    notify_device_id = str(row[1])
                    triggered_parameter = str(row[2])
                    comparator = str(row[3])
                    threshold = row[4]
                    notify_channel = str(row[5])
                    notify_address = row[6]
                    notify_heartbeat = row[7] if row[7] is not None else self.notify_heartbeat
                    _event_has_triggered = False
                    print "{} triggered_parameter:{} self.get_variable(triggered_parameter):{} comparator:{} threshold:{}"\
                        .format(agent_id, triggered_parameter, self.get_variable(triggered_parameter), comparator, threshold)
                    #check whether message is already sent
                    try:
                        if (datetime.datetime.now() - self.time_sent_notifications[
                            event_id]).seconds > notify_heartbeat:
                            if notify_device_id == agent_id:
                                if comparator == "<":
                                    threshold = float(threshold)
                                    if self.get_variable(triggered_parameter) < threshold: _event_has_triggered = True
                                elif comparator == ">":
                                    threshold = float(threshold)
                                    if self.get_variable(triggered_parameter) > threshold: _event_has_triggered = True
                                    print "{} triggered_parameter:{} self.get_variable(triggered_parameter):{} comparator:{} threshold:{}"\
                                        .format(agent_id, triggered_parameter, self.get_variable(triggered_parameter), comparator, threshold)
                                    print "{} _event_has_triggerered {}".format(agent_id,_event_has_triggered)
                                elif comparator == "<=":
                                    threshold = float(threshold)
                                    if self.get_variable(triggered_parameter) <= threshold: _event_has_triggered = True
                                elif comparator == ">=":
                                    threshold = float(threshold)
                                    if self.get_variable(triggered_parameter) >= threshold: _event_has_triggered = True
                                elif comparator == "is":
                                    if threshold == "True":
                                        threshold = True
                                    elif threshold == "False":
                                        threshold = False
                                    else:
                                        threshold = str(threshold)
                                    if self.get_variable(triggered_parameter) is threshold: _event_has_triggered = True
                                elif comparator == "isnot":
                                    if threshold == "True":
                                        threshold = True
                                    elif threshold == "False":
                                        threshold = False
                                    else:
                                        threshold = str(threshold)
                                    if self.get_variable(
                                            triggered_parameter) is not threshold: _event_has_triggered = True
                                else:
                                    pass
                                if _event_has_triggered:  # notify the user if triggered
                                    #step2 notify user to notify_channel at notify_address with period notify_heartbeat
                                    if notify_channel == 'email':
                                        _email_text = '{} notification event triggered_parameter: {}, comparator: {}, ' \
                                                      'threshold: {}\n now the current status of triggered_parameter: {} is {}' \
                                            .format(agent_id, triggered_parameter, comparator, threshold,
                                                    triggered_parameter, self.get_variable(triggered_parameter))
                                        emailService = EmailService()
                                        emailService.sendEmail(email_fromaddr, notify_address, email_username,
                                                               email_password,
                                                               self.subject, _email_text, email_mailServer)
                                        # self.send_notification_status = True
                                        #TODO store time_send_notification for each event
                                        self.time_sent_notifications[event_id] = datetime.datetime.now()
                                        print "time_sent_notifications is {}".format(
                                            self.time_sent_notifications[event_id])
                                        print('{} >> sent notification message for {}'.format(agent_id, event_name))
                                        print(
                                        '{} notification event triggered_parameter: {}, comparator: {}, threshold: {}'
                                        .format(agent_id, triggered_parameter, comparator, threshold))
                                    else:
                                        print "{} >> notification channel: {} is not supported yet".format(agent_id,
                                                                                                           notify_channel)
                                else:
                                    print "{} >> Event is not triggered".format(agent_id)
                            else:
                                "{} >> this event_id {} is not for this device".format(agent_id, event_id)
                        else:
                            "{} >> Email is already sent, waiting for another heartbeat period".format(agent_id)
                    except:
                        #step1 compare triggered_parameter with comparator to threshold
                        #step1.1 classify comparator <,>,<=,>=,is,isnot
                        #case1 comparator <
                        print "{} >> first time trigger notification".format(agent_id)
                        if notify_device_id == agent_id:
                            if comparator == "<":
                                threshold = float(threshold)
                                if self.get_variable(triggered_parameter) < threshold: _event_has_triggered = True
                            elif comparator == ">":
                                threshold = float(threshold)
                                if self.get_variable(triggered_parameter) > threshold: _event_has_triggered = True
                                print "{} triggered_parameter:{} self.get_variable(triggered_parameter):{} comparator:{} threshold:{}"\
                                        .format(agent_id, triggered_parameter, self.get_variable(triggered_parameter), comparator, threshold)
                                print "{} _event_has_triggerered {}".format(agent_id,_event_has_triggered)
                            elif comparator == "<=":
                                threshold = float(threshold)
                                if self.get_variable(triggered_parameter) <= threshold: _event_has_triggered = True
                            elif comparator == ">=":
                                threshold = float(threshold)
                                if self.get_variable(triggered_parameter) >= threshold: _event_has_triggered = True
                            elif comparator == "is":
                                if threshold == "True":
                                    threshold = True
                                elif threshold == "False":
                                    threshold = False
                                else:
                                    threshold = str(threshold)
                                if self.get_variable(triggered_parameter) is threshold: _event_has_triggered = True
                            elif comparator == "isnot":
                                if threshold == "True":
                                    threshold = True
                                elif threshold == "False":
                                    threshold = False
                                else:
                                    threshold = str(threshold)
                                if self.get_variable(triggered_parameter) is not threshold: _event_has_triggered = True
                            else:
                                pass
                            print "{} >> _event_has_triggered {}".format(agent_id, _event_has_triggered)
                            if _event_has_triggered:  # notify the user if triggered
                                #step2 notify user to notify_channel at notify_address with period notify_heartbeat
                                if notify_channel == 'email':
                                    _email_text = '{} notification event triggered_parameter: {}, comparator: {}, ' \
                                                  'threshold: {}\n now the current status of triggered_parameter: {} is {}' \
                                        .format(agent_id, triggered_parameter, comparator, threshold,
                                                triggered_parameter, self.get_variable(triggered_parameter))
                                    emailService = EmailService()
                                    emailService.sendEmail(email_fromaddr, notify_address, email_username,
                                                           email_password,
                                                           self.subject, _email_text, email_mailServer)
                                    # self.send_notification_status = True
                                    #store time_send_notification for each event
                                    self.time_sent_notifications[event_id] = datetime.datetime.now()
                                    print "{} >> time_sent_notifications is {}".format(agent_id, self.time_sent_notifications[event_id])
                                    print('{} >> sent notification message for {}'.format(agent_id, event_name))
                                    print('{} notification event triggered_parameter: {}, comparator: {}, threshold: {}'
                                          .format(agent_id, triggered_parameter, comparator, threshold))
                                else:
                                    print "{} >> notification channel: {} is not supported yet".format(agent_id,
                                                                                                       notify_channel)
                            else:
                                print "{} >> Event is not triggered".format(agent_id)
                        else:
                            "{} >> this event_id {} is not for this device".format(agent_id, event_id)
                else:
                    pass

    Agent.__name__ = 'Thermostat Agent'
    return Agent(**kwargs)
Esempio n. 11
0
def AhpAgent(config_path, **kwargs):
    config = utils.load_config(config_path)
    agent_id = config['agentid']
    threshold = 14

    def get_config(name):
        try:
            value = kwargs.pop(name)
            return value
        except KeyError:
            return config[name]

    # This agent uses the implementation of the ahp agorithm to determine what (if any)
    # devices in a building are to be curtailed to shed electrical load. The criteria
    # matrix is expected to be in a table in an excel spredsheet (to simplify data entry by the end user)
    # The agent will listen for information being sent from the bacnet drivers regarding the desired
    # devices, and on a periodic basis (to be set in the configuration file) perform calculations to
    # select the device(s) that are candidates for curtailment. This initial implementation will
    # be focused on heat pumps in a commercial building. As a result, the agent will also need
    # to keep track of the times when the compressor was stopped to allow for a period of equilibrium.
    #
    # TODO:
    #   * Have the agent open and read the criteria matrix from the excel spreadsheet
    #   * Subscribe to the topics that provide the information from the bacnet drivers
    #   * Store the readings from the heat pumps that are to be watched

    class Agent(PublishMixin, BaseAgent):
        """Agent that performs curtailment in a building using the AHP algorithm"""
        def __init__(self, **kwars):
            super(Agent, self).__init__(**kwargs)

        def setup(self):
            # Load criteria matrix
            excel_doc = get_config("excel_doc")
            self.output_log = open("ahp_algorithm.log", "w")
            self.logger = LoggerWriter(_log, logging.DEBUG)

            self.logger.write("Testing")

            (self.criteria_labels, self.criteria_matrix) = extract_criteria_matrix(excel_doc)
            self.criteria_matrix_sums = calc_column_sums(self.criteria_matrix)

            # Validate criteria matrix
            if(not validate_input(self.criteria_matrix, self.criteria_matrix_sums, True, criteria_labels, criteria_labelstring, matrix_rowstring, display_dest=self.logger)):
                # TODO: log a warning indicatin invalid matrix input
                pass

            # TODO: Load device list. Right now, it will come from the config file.
            #       Eventually this will come from the excel spreadsheet
            self.device_list = get_config('device_list')

            self.deviceLabels = [row[0] for row in self.device_list]
            self.deviceDataHandlers = {}
            for deviceRow in self.device_list:
                self.deviceDataHandlers[deviceRow[0]] = DeviceData(deviceRow[0], deviceRow[1], logger=self.logger)
            super(Agent, self).setup()

        # TODO: Set up subscriptions. Need to subscribe to sigma4/all
        @match_glob('RTU/PNNL/BOCC/Sigma4/HP*/all')
        def process_data(self, topic, headers, message, match):
            data = jsonapi.loads(message[0])
            # print >> self.logger, topic, message

            device_label = topic.split('/')[4]

            if device_label in self.deviceDataHandlers:
            # look up device
                device = self.deviceDataHandlers[device_label]

                # call device process_data method
                device.process_data(time.time(), data)

        @periodic(600)
        def schedule_algorithm(self):
            # submit request for schedule to change points
            headers = {
                'AgentID': agent_id,
                'type':  'NEW_SCHEDULE',
                'requesterID': agent_id,
                'taskID': agent_id,
                'priority': 'LOW_PREEMPT'
            }

            # Build up schedule
            start = str(datetime.datetime.now())
            end = str(datetime.datetime.now() + datetime.timedelta(minutes=1))

            # msg = [['PNNL/BOCC/Sigma4/HP1', start, end]]
            msg = []
            for label in self.deviceLabels:
                msg.append(['PNNL/BOCC/Sigma4/' + label, start, end])

            print >> self.logger, "Submitting schedule"
            self.publish_json(topics.ACTUATOR_SCHEDULE_REQUEST(), headers, msg)
            print >> self.logger, "Schedule submitted"

            # Example from afddagent
            # self.task_timer = self.periodic_timer(60, self.publish_json, topics.ACTUATOR_SCHEDULE_REQUEST(), headers,[["{campus}/{building}/{unit}".format(**rtu_path),self.start,self.end]])

        @match_headers({headers_mod.REQUESTER_ID: agent_id})
        @match_exact(topics.ACTUATOR_SCHEDULE_RESULT())
        def handle_scheduler_response(self, topic, headers, message, match):
            msg = jsonapi.loads(message[0])
            response_type = headers.get('type', 0)

            if response_type == 'NEW_SCHEDULE':
                if msg.get('result', 0) == 'SUCCESS':
                    self.logger.write("Schedule Successful")
                    self.ready = True

        @match_headers({headers_mod.REQUESTER_ID: agent_id})
        @match_start('RTU/actuators/schedule/announce')
        def do_algorithm(self, topic, headers, message, match):
            if not (headers[headers_mod.REQUESTER_ID] == agent_id):
                return

            if self.ready:
                self.ready = False
                print >> self.logger, "====== Calculate Curtailment ======"
                # The actual ahp algorithm stuff will happen as part of the response to a successful request for a schedule.

                device_matrix = generateMatrix(self.device_list, self.deviceDataHandlers)

                print >> self.logger, self.deviceDataHandlers
                scores = demo_ahp(self.criteria_matrix, device_matrix, self.deviceLabels, self.criteria_labels, criteria_labelstring, matrix_rowstring, display_dest=self.logger)
                # def demo_ahp(criteria_matrix, device_matrix, devices, criteria_labels="", criteria_labelstring="", matrix_rowstring="", display_dest=sys.stdout):
                pwr_saved = 0
                device_offsets = []
                for device in scores:
                    if pwr_saved >= threshold:
                        device_offsets.append(0.0)
                    else:
                        if self.deviceDataHandlers[device[0]].curtailWithThreshold():
                            device_offsets.append(3.0)
                        else:
                            device_offsets.append(0.0)

                        pwr_saved += 7

                header = {'requesterID': agent_id}
                device_count = 0
                for (device, score) in scores:
                    path = 'RTU/actuators/set/PNNL/BOCC/Sigma4/' + device + '/Volttron_Temp_Offset'
                    print >> self.logger, 'Updating %s with %d' % (path, device_offsets[device_count])
                    self.publish(path, header, str(device_offsets[device_count]))
                    device_count += 1

                headers = {
                    'AgentID': agent_id,
                    'type':  'CANCEL_SCHEDULE',
                    'requesterID': agent_id,
                    'taskID': agent_id,
                }

                self.publish_json(topics.ACTUATOR_SCHEDULE_REQUEST(), headers, [])

            else:
                self.ready = False



    Agent.__name__ = 'AhpAgent'
    return Agent(**kwargs)
Esempio n. 12
0
def dragent(config_path, **kwargs):
    """DR application for time of use pricing"""
    config = utils.load_config(config_path)
    agent_id = config['agentid']
    rtu_path = dict((key, config[key])
                    for key in ['campus', 'building', 'unit'])
   
    class Agent(PublishMixin, BaseAgent):
        """Class agent"""
        def __init__(self, **kwargs):
            super(Agent, self).__init__(**kwargs)
            self.lock_timer = None
            self.lock_acquired = False
            self.tasklet = None
            self.data_queue = green.WaitQueue(self.timer)
            self.value_queue = green.WaitQueue(self.timer)

        def setup(self):
            """acquire lock fom actuator agent"""
            super(Agent, self).setup()
            headers = {
                    'Content-Type': 'text/plain',
                    'requesterID': agent_id,
            }
            self.lock_timer = self.periodic_timer(1, self.publish, topics.ACTUATOR_LOCK_ACQUIRE(**rtu_path), headers)
            
        @matching.match_exact(topics.ACTUATOR_LOCK_ACQUIRE(**rtu_path))
        def __on_lock_sent(self, topic, headers, message, match):
            """lock recieved"""
            self.lock_timer.cancel()

        @matching.match_exact(topics.ACTUATOR_LOCK_RESULT(**rtu_path))
        def __on_lock_result(self, topic, headers, message, match):
            """lock result"""
            msg = jsonapi.loads(message[0])
            holding_lock = self.lock_acquired
            if headers['requesterID'] == agent_id:
                self.lock_acquired = msg == 'SUCCESS'
            elif msg == 'SUCCESS':
                self.lock_acquired = False
            if self.lock_acquired and not holding_lock:
                self.start()

        @matching.match_exact(topics.RTU_VALUE(point='all', **rtu_path))
        def __on_new_data(self, topic, headers, message, match):
            """watching for new data"""
            data = jsonapi.loads(message[0])
            self.data_queue.notify_all(data)
            
        @matching.match_glob(topics.ACTUATOR_VALUE(point='*', **rtu_path))
        def __on_set_result(self, topic, headers, message, match):
            """set value in conroller"""
            self.value_queue.notify_all((match.group(1), True))
    
        @matching.match_glob(topics.ACTUATOR_ERROR(point='*', **rtu_path))
        def __on_set_error(self, topic, headers, message, match):
            """watch for actuator error"""
            self.value_queue.notify_all((match.group(1), False))

        def __sleep(self, timeout=None):
            """built in sleep in green"""
            #_log.debug('sleep({})'.format(timeout))
            green.sleep(timeout, self.timer)

        def __get_new_data(self, timeout=None):
            """wait for new data"""
            _log.debug('get_new_data({})'.format(timeout))
            return self.data_queue.wait(timeout)

        def __command_equip(self, point_name, value, timeout=None):
            """set value in controller"""
            _log.debug('set_point({}, {}, {})'.format(point_name, value, timeout))
            headers = {
                    'Content-Type': 'text/plain',
                    'requesterID': agent_id,
            }
            self.publish(topics.ACTUATOR_SET(point=point_name, **rtu_path),
                         headers, str(value))
            try:
                return self.value_queue.wait(timeout)
            except green.Timeout:
                return None
            
        def __time_out(self):
            """if timeout occurs"""
            global fan1_norm
            global fan2_norm
            global csp_norm 
            global min_damper
            if fan1_norm == 0:
                self.__sleep(600)#If controller loses volttron heartbeat will reset
                self.start()     #wait at least this long 
            else:
                try:
                    self.__command_equip('CoolSupplyFanSpeed1', fan1_norm)
                except green.Timeout:
                    self.__sleep(600)
                    self.start()
                try:
                    self.__command_equip('CoolSupplyFanSpeed2', fan2_norm)
                except green.Timeout:
                    self.__sleep(600)
                    self.start()
                try:
                    self.__command_equip('ESMDamperMinPosition', min_damper)
                except green.Timeout:
                    self.__sleep(600)
                    self.start()
                try:
                    self.__command_equip('ReturnAirCO2Stpt', csp_norm)
                except green.Timeout:
                    self.__sleep(600)
                    self.start() 
            
         
        @matching.match_exact(topics.RTU_VALUE(point='Occupied', **rtu_path))
        def __overide(self, topic, headers, message, match):
            """watch for override from controller"""
            data = jsonapi.loads(message[0])
            if not bool(data):
                self.tasklet = greenlet.greenlet(self.__on_override)
                self.tasklet.switch()
                

        def __on_override(self): 
            global fan1_norm
            global fan2_norm
            global csp_norm
            global min_damper
            global override_flag
            
            if fan1_norm != 0 and not override_flag:
                override_flag = True
                _log.debug('Override initiated')
                try:
                    self.__command_equip('CoolSupplyFanSpeed1', fan1_norm)
                except green.Timeout:
                    self.__sleep(43200) #if override wait for 12 hours then resume
                    self.start()        #catalyst will default to original operations with no volttron heatbeat
                try:
                    self.__command_equip('CoolSupplyFanSpeed2', fan2_norm)
                except green.Timeout:
                    self.__sleep(43200)
                    self.start()
                try:
                    self.__command_equip('ESMDamperMinPosition', min_damper )
                except green.Timeout:
                    self.__sleep(43200)
                    self.start()
                try:
                    self.__command_equip('ReturnAirCO2Stpt', csp_norm)
                except green.Timeout:
                    self.__sleep(43200)
                    self.start()
            elif fan1_norm == 0 and not override_flag:
                override_flag = True
                self.__sleep(43200)
                self.start()
                
                    
        def start(self):
            """Starting point for DR application"""
            global override
            override_flag = False
            self.tasklet = greenlet.greenlet(self.__go)
            self.tasklet.switch()
        
        def __go(self):
            """start main DR procedure"""
            #self.__command_equip('CoolSupplyFanSpeed1', 75)
            #self.__command_equip('CoolSupplyFanSpeed2', 90)
            #self.__command_equip('ESMDamperMinPosition', 5)
            global fan1_norm
            global fan2_norm
            global csp_norm
            global min_damper
            try:
                self.__command_equip('ReturnAirCO2Stpt', 74)
            except green.Timeout:
                self.__time_out()
            try:
                voltron_data = self.__get_new_data()
            except green.Timeout:
                self.__time_out()
                # Gracefully handle exception
            min_damper = float(voltron_data["ESMDamperMinPosition"]) 
            fan1_norm = float(voltron_data["CoolSupplyFanSpeed1"])
            fan2_norm = float(voltron_data["CoolSupplyFanSpeed2"])
            csp_norm = float(voltron_data["ReturnAirCO2Stpt"])
            _log.debug("Zone normal cooling temperature setpoint:  " + repr(csp_norm))
            _log.debug("Supply fan cooling speed 1:  " + repr(fan1_norm))
            _log.debug("Supply fan cooling speed 2:  " + repr(fan2_norm))
            _log.debug("Normal minimum damper position:  " + repr(min_damper))
            self.tasklet = greenlet.greenlet(self.get_signal)
            self.tasklet.switch()
                
        def __pre_cpp_timer(self):
            """Schedule to run in get_signal"""
            _log.debug("Pre-cooling for CPP Event")  #pre-cool change cooling set point
            self.tasklet = greenlet.greenlet(self.__pre_csp)
            self.tasklet.switch()
            self.pre_timer = self.periodic_timer(settings.pre_cooling_time, self.__pre_cpp_cooling)
            
        def __pre_cpp_cooling(self):
            """start pre cooling procedure"""
            self.tasklet = greenlet.greenlet(self.__pre_csp)
            self.tasklet.switch()
            
        def __pre_csp(self):
            """set cooling temp. set point"""
            self.__sleep(1)
            try:
                voltron_data = self.__get_new_data()
            except green.Timeout:
                self.__time_out()
            csp_now = float(voltron_data["ReturnAirCO2Stpt"]) 
            if csp_now > settings.csp_pre:
                try:
                    csp = csp_now - cooling_slope
                    self.__command_equip("ReturnAirCO2Stpt", csp)
                except green.Timeout:
                    self.__time_out()
            elif csp_now <= settings.csp_pre:
                try:
                    self.__command_equip("ReturnAirCO2Stpt", settings.csp_pre)
                except green.Timeout:
                    self.__time_out()
                self.pre_timer.cancel()
                
        def __accelerated_pre_cpp_timer(self):
            """if DR signal is received after normal pre"""
            _log.debug("Pre-cooling for CPP Event")  #pre-cool change cooling set point
            self.tasklet = greenlet.greenlet(self.__accelerated_pre_csp)
            self.tasklet.switch()
            self.pre_timer = self.periodic_timer(settings.pre_time, self.__accelerated_cpp_cooling) 
              
        def __accelerated_cpp_cooling(self):
            """start accelerated pre-cooling"""
            self.tasklet = greenlet.greenlet(self.__accelerated_pre_csp)
            self.tasklet.switch()
                
        def __accelerated_pre_csp(self):
            """set cooling temp set point"""
            _log.debug("Accelerated pre-cooling for CPP Event")
            global accel_slope
            self.__sleep(2)
            try:
                voltron_data = self.__get_new_data()
            except green.Timeout:
                self.__time_out()  
            csp_now = float(voltron_data["ReturnAirCO2Stpt"]) 
            csp  = csp_now - accel_slope
            if csp_now > settings.csp_pre:
                try:
                    self.__command_equip("ReturnAirCO2Stpt", csp)
                except green.Timeout:
                    self.__time_out()
            elif csp_now <= settings.csp_pre:
                try:
                    self.__command_equip("ReturnAirCO2Stpt", settings.csp_pre)
                except green.Timeout:
                    self.__time_out()
                self.pre_timer.cancel() 

        def __during_cpp_timer(self):
            """during CPP scheduled in get_signal"""
            self.tasklet = greenlet.greenlet(self.__during_cpp)
            self.tasklet.switch() 
                  
        def __during_cpp(self):
            """start CPP procedure"""
            _log.debug("During CPP Event")# remove when done testing
            self.__sleep(2)
            global fan1_norm
            global fan2_norm
            cpp_damper = settings.cpp_damper
            fan_reduction = settings.fan_reduction
            cpp_csp = settings.cpp_csp
            cpp_fan1 = fan1_norm- fan1_norm * fan_reduction
            cpp_fan2 = fan2_norm- fan2_norm * fan_reduction
            self.__sleep(1)
            try:
                self.__command_equip("CoolSupplyFanSpeed1", cpp_fan1)
            except green.Timeout:
                self.__time_out()
            try:
                self.__command_equip("CoolSupplyFanSpeed2", cpp_fan2)
            except green.Timeout:
                self.__time_out()
            try:
                self.__command_equip("ReturnAirCO2Stpt", cpp_csp)
            except green.Timeout:
                self.__time_out()
            try:
                self.__command_equip('ESMDamperMinPosition', cpp_damper)
            except green.Timeout:
                self.__time_out()
            
        def __after_cpp_timer(self):
            """after CPP scheduled in get_signal"""
            self.tasklet = greenlet.greenlet(self.__restore_fan_damper)
            self.tasklet.switch()
            _log.debug("After CPP Event, returning to normal operations")
            self.tasklet = greenlet.greenlet(self.__restore_cooling_setpoint)
            self.tasklet.switch()
            timer = settings.after_time
            self.after_timer = self.periodic_timer(timer, self.__after_cpp_cooling)
             
        def __after_cpp_cooling(self):
            """Start after CPP procedure"""
            _log.debug("After_CPP_COOLING")
            self.tasklet = greenlet.greenlet(self.__restore_cooling_setpoint)
            self.tasklet.switch()
            
        def __restore_fan_damper(self):
            """restore original fan speeds"""
            global fan1_norm
            global fan2_norm
            global min_damper
            self.__sleep(2) # so screen _log.debugs in correct order remove after testing.
            try:
                self.__command_equip("ESMDamperMinPosition", min_damper)
            except green.Timeout:
                self.__time_out()
            try:
                self.__command_equip("CoolSupplyFanSpeed1", fan1_norm)
            except green.Timeout:
                self.__time_out()
            try:
                self.__command_equip("CoolSupplyFanSpeed2", fan2_norm)
            except green.Timeout:
                self.__time_out()
            
        def __restore_cooling_setpoint(self):
            """restore normal cooling temp setpoint"""
            global csp_norm
            self.__sleep(2) #remove after testing
            try:
                voltron_data = self.__get_new_data()
            except green.Timeout:
                self.__time_out()
            csp_now = float(voltron_data["ReturnAirCO2Stpt"]) 
            if csp_now > csp_norm:
                csp = csp_now - cooling_slope
                try:
                    self.__command_equip("ReturnAirCO2Stpt", csp)
                except green.Timeout:
                    self.__time_out()
            elif csp_now <= csp_norm:
                self.after_timer.cancel()
                try:
                    self.__command_equip("ReturnAirCO2Stpt", csp_norm)
                except green.Timeout:
                    self.__time_out()
                
        def get_signal(self):
            """get and format DR signal and schedule DR proc."""
            #Pull signal from source
            self.__sleep(2) #remove after testing
            global csp_norm
            global cooling_slope
            global accel_slope
            time_now = time.mktime(datetime.datetime.now().timetuple())
            time_pre = time.mktime(datetime.datetime.now().replace(hour = settings.pre_cpp_hour, minute = 23, second=0, microsecond = 0).timetuple())
            time_event = time.mktime(datetime.datetime.now().replace(hour = settings.during_cpp_hour, minute = 25, second = 0, microsecond = 0).timetuple())
            time_after = time.mktime(datetime.datetime.now().replace(hour = settings.after_cpp_hour, minute = 27, second = 0, microsecond = 0).timetuple())
            if (settings.signal and time_now<time_pre):
                _log.debug ("Scheduling1") 
                time_step = settings.pre_cooling_time/3600
                #cooling_slope = (csp_norm-settings.csp_pre)/((((time_event-time_pre)/3600)-0.5)*time_step) 
                cooling_slope = 1  # for testing use a constant
                temp = ((time_event-time_pre)/3600)
                _log.debug ("cooling slope: "+ repr(cooling_slope))
                pre_cpp_time = datetime.datetime.now().replace(hour = settings.pre_cpp_hour, minute = 23, second = 0, microsecond = 0)
                self.schedule(pre_cpp_time, sched.Event(self.__pre_cpp_timer))
                during_cpp_time = datetime.datetime.now().replace(hour = settings.during_cpp_hour, minute=25, second = 0, microsecond = 0)
                self.schedule(during_cpp_time, sched.Event(self.__during_cpp_timer))
                after_cpp_time = datetime.datetime.now().replace(hour = settings.after_cpp_hour, minute = 27, second = 0, microsecond = 0)
                self.schedule(after_cpp_time, sched.Event(self.__after_cpp_timer))
                #self.start_timer.cancel()
            elif(settings.signal and time_now>time_pre and time_now<time_event):
                _log.debug("Scheduling2")
                #self.start_timer.cancel()
                #accel_slope = (csp_norm-settings.csp_pre)/((time_event-time_now)/(3600))
                accel_slope = 2 #for testing use a constant
                during_cpp_time = datetime.datetime.now().replace(hour = settings.during_cpp_hour, minute = 36, second =20, microsecond = 0)
                self.schedule(during_cpp_time, sched.Event(self.__during_cpp_timer))
                after_cpp_time = datetime.datetime.now().replace(hour = settings.after_cpp_hour, minute = 39, second = 10, microsecond = 0)
                self.schedule(after_cpp_time, sched.Event(self.__after_cpp_timer))
                self.__accelerated_pre_cpp_timer() 
            elif(settings.signal and time_now>time_event and time_now<time_after):
                _log.debug("Too late to pre-cool!")
                #self.start_timer.cancel()
                after_cpp_time = datetime.datetime.now().replace(hour=settings.after_cpp_hour, minute = 17, second = 0, microsecond = 0)
                self.schedule(after_cpp_time, sched.Event(self.__after_cpp_timer))
                self.tasklet = greenlet.greenlet(self.__during_cpp)
                self.tasklet.switch()
            else:
                _log.debug("CPP Event Is Over")
                #self.start_timer.cancel()
                self.__sleep(60)
                self.get_signal()
                
    Agent.__name__ = 'dragent'
    return Agent(**kwargs)
Esempio n. 13
0
def DemandResponseAgent(config_path, **kwargs):
    """DR application for time of use pricing"""
    config = utils.load_config(config_path)
    agent_id = config['agentid']
    rtu_path = dict(
        (key, config[key]) for key in ['campus', 'building', 'unit'])
    command_timeout = config.get('command-timeout',
                                 settings.default_command_timeout)
    data_timeout = config.get('data-timeout', settings.default_data_timeout)

    csp_pre = config.get('csp_pre', settings.csp_pre)
    csp_cpp = config.get('csp_cpp', settings.csp_cpp)
    damper_cpp = config.get('damper_cpp', settings.damper_cpp)
    fan_reduction = config.get('fan_reduction', settings.fan_reduction)
    pre_time = config.get('pre_time', settings.pre_time)
    after_time = config.get('after_time', settings.after_time)
    time_step = config.get('time_step', settings.after_time)
    Schedule = config.get('Schedule')

    class Agent(PublishMixin, BaseAgent):
        """Class agent"""
        def __init__(self, **kwargs):
            super(Agent, self).__init__(**kwargs)
            self.schedule_state = False
            self.fan1_norm = 0
            self.fan2_norm = 0
            self.csp_norm = 0
            self.accel_slope = 0
            self.cooling_slope = 0
            self.min_damper = 0
            self.override_flag = False
            self.lock_timer = None
            self.lock_acquired = False
            self.timers = []
            self.tasks = []
            self.tasklet = None
            self.data_queue = green.WaitQueue(self.timer)
            self.value_queue = green.WaitQueue(self.timer)
            self.running = False

        def setup(self):
            """acquire lock fom actuator agent"""
            super(Agent, self).setup()

        @matching.match_exact(topics.RTU_VALUE(point='CoolCall1', **rtu_path))
        def dr_signal(self, topic, headers, message, match):
            data = jsonapi.loads(message[0])
            if not self.running and bool(data):
                print("start")
                self.running = True
                time_now = time.mktime(datetime.datetime.now().timetuple())
                self.update_schedule_state(time_now)
                #self.schedule(next_time, self.update_schedule_state)
                if (self.schedule_state):
                    self.start()
                else:
                    _log.debug(
                        "DR signal is False or day is not an occupied day")

        def update_schedule_state(self, unix_time):
            headers = {
                'Content-Type': 'text/plain',
                'requesterID': agent_id,
            }
            now = datetime.datetime.fromtimestamp(unix_time)
            day = now.weekday()
            if Schedule[day]:
                self.schedule_state = True
                #TODO: set this up to handle platform not running.
                #This will hang after a while otherwise.
                self.lock_timer = super(Agent, self).periodic_timer(
                    1, self.publish, topics.ACTUATOR_LOCK_ACQUIRE(**rtu_path),
                    headers)
            else:
                self.schedule_state = False
                self.publish(topics.ACTUATOR_LOCK_RELEASE(**rtu_path), headers)

        @matching.match_exact(topics.ACTUATOR_LOCK_ACQUIRE(**rtu_path))
        def _on_lock_sent(self, topic, headers, message, match):
            """lock request received"""
            self.lock_timer.cancel()

        @matching.match_exact(topics.ACTUATOR_LOCK_RESULT(**rtu_path))
        def _on_lock_result(self, topic, headers, message, match):
            """lock result"""
            msg = jsonapi.loads(message[0])
            holding_lock = self.lock_acquired
            if headers['requesterID'] == agent_id:
                self.lock_acquired = msg == 'SUCCESS'
            elif msg == 'SUCCESS':
                self.lock_acquired = False
            if self.lock_acquired and not holding_lock:
                self.start()

        @matching.match_exact(topics.RTU_VALUE(point='all', **rtu_path))
        def _on_new_data(self, topic, headers, message, match):
            """watching for new data"""
            data = jsonapi.loads(message[0])
            self.data_queue.notify_all(data)

        @matching.match_glob(topics.ACTUATOR_VALUE(point='*', **rtu_path))
        def _on_set_result(self, topic, headers, message, match):
            """set value in conroller"""
            self.value_queue.notify_all((match.group(1), True))

        @matching.match_glob(topics.ACTUATOR_ERROR(point='*', **rtu_path))
        def _on_set_error(self, topic, headers, message, match):
            """watch for actuator error"""
            self.value_queue.notify_all((match.group(1), False))

        def _sleep(self, timeout=None):
            """built in sleep in green"""
            _log.debug('sleep({})'.format(timeout))
            green.sleep(timeout, self.timer)

        def _get_new_data(self, timeout=data_timeout):  #timeout=data_timeout
            """wait for new data"""
            _log.debug('get_new_data({})'.format(timeout))
            return self.data_queue.wait(timeout)

        def _command_equip(self, point_name, value, timeout):
            """set value in controller"""
            _log.debug('set_point({}, {}, {})'.format(point_name, value,
                                                      timeout))
            headers = {
                'Content-Type': 'text/plain',
                'requesterID': agent_id,
            }
            self.publish(topics.ACTUATOR_SET(point=point_name, **rtu_path),
                         headers, str(value))
            while True:
                point, success = self.value_queue.wait(timeout)
                if point == point_name:
                    if success:
                        return
                    raise CommandSetError()

        def _time_out(self):
            """if timeout occurs"""
            if (self.fan1_norm and self.fan2_norm and self.min_damper
                    and self.csp_norm):
                try:
                    self._command_equip('CoolSupplyFanSpeed1', self.fan1_norm)
                    self._command_equip('CoolSupplyFanSpeed2', self.fan2_norm)
                    self._command_equip('ESMDamperMinPosition',
                                        self.min_damper)
                    self._command_equip('StandardDamperMinPosition',
                                        self.csp_norm)
                except green.Timeout:
                    pass
            for timer in self.timers:
                timer.cancel()
            del self.timers[:]
            current = greenlet.getcurrent()
            for task in self.tasks:
                if task is not current:
                    task.parent = current
                    task.throw()
            print 'adding current task to task list'
            self.tasks[:] = [current]
            self._sleep(
                600)  #If controller loses volttron heartbeat will reset
            self.running = False

        @matching.match_exact(
            topics.RTU_VALUE(point='Occupied', **rtu_path)
        )  # for now look for Occuppied, DR Override will be added
        def _override(self, topic, headers, message, match):
            """watch for override from controller"""
            data = jsonapi.loads(message[0])
            if not bool(data):
                self.greenlet(self._on_override)

        def _on_override(self):
            if not self.override_flag:
                self.override_flag = True
                _log.debug("Override initiated")
                for timer in self.timers:
                    timer.cancel()
                del self.timers[:]
                current = greenlet.getcurrent()
                for task in self.tasks:
                    if task is not current:
                        task.parent = current
                        task.throw()
            if self.fan1_norm:
                try:
                    self._command_equip('CoolSupplyFanSpeed1', self.fan1_norm)
                    self._command_equip('CoolSupplyFanSpeed2', self.fan2_norm)
                    self._command_equip('ESMDamperMinPosition',
                                        self.min_damper)
                    self._command_equip('ReturnAirCO2Stpt', self.csp_norm)
                except green.Timeout:
                    self._sleep(
                        43200)  #if override wait for 12 hours then resume
                    self._go(
                    )  #catalyst will default to original operations with no volttron heatbeat
                self._sleep(43200)
                self.override_flag = False
                self.running = False
            elif not self.fan1_norm and not self.override_flag:
                self.override_flag = True
                self._sleep(43200)
                self.override_flag = False
                self.running = False

        def start(self):
            """Starting point for DR application"""
            self.override_flag = False
            self.greenlet(self._go)

        def _go(self):
            """start main DR procedure"""
            self._command_equip('StandardDamperChangeOverSetPoint', 75, 20)
            self._command_equip('CoolSupplyFanSpeed2', 90, 20)
            self._command_equip('CoolSupplyFanSpeed1', 75, 20)
            self._command_equip('StandardDamperMinPosition', 65, 20)
            try:
                self._command_equip('ESMDamperMinPosition', 5, 20)

                voltron_data = self._get_new_data()
            except CommandSetError:
                self._time_out()
            except green.Timeout:
                self._time_out()
            self.min_damper = float(voltron_data["ESMDamperMinPosition"])
            self.fan1_norm = float(voltron_data["CoolSupplyFanSpeed1"])
            self.fan2_norm = float(voltron_data["CoolSupplyFanSpeed2"])
            self.csp_norm = float(voltron_data["ReturnAirCO2Stpt"])
            _log.debug("Zone normal cooling temperature setpoint:  " +
                       repr(self.csp_norm))
            _log.debug("Supply fan cooling speed 1:  " + repr(self.fan1_norm))
            _log.debug("Supply fan cooling speed 2:  " + repr(self.fan2_norm))
            _log.debug("Normal minimum damper position:  " +
                       repr(self.min_damper))
            self.get_signal()

        def _pre_cpp_timer(self):
            """Schedule to run in get_signal"""
            _log.debug("Pre-cooling for CPP Event"
                       )  #pre-cool change cooling set point
            self._pre_csp()
            self.pre_timer = self.periodic_timer(settings.pre_time,
                                                 self._pre_cpp_cooling)

        def _pre_cpp_cooling(self):
            """start pre cooling procedure"""
            self.greenlet(self._pre_csp)

        def _pre_csp(self):
            """set cooling temp set point"""
            try:
                voltron_data = self._get_new_data()
            except green.Timeout:
                self._time_out()
            csp_now = float(voltron_data["ReturnAirCO2Stpt"])
            if csp_now > csp_pre and not csp < csp_pre:
                try:
                    csp = csp_now - self.cooling_slope
                    self._command_equip("ReturnAirCO2Stpt", csp)
                except green.Timeout:
                    self._time_out()
            elif csp_now <= csp_pre and not csp < csp_pre:
                try:
                    self._command_equip("ReturnAirCO2Stpt", settings.csp_pre)
                except green.Timeout:
                    self._time_out()
                self.pre_timer.cancel()

        def _accelerated_pre_cpp_timer(self):
            """if DR signal is received after normal pre"""
            _log.debug("Pre-cooling for CPP Event"
                       )  #pre-cool change cooling set point
            self._accelerated_pre_csp()
            self.pre_timer = self.periodic_timer(settings.pre_time,
                                                 self._accelerated_cpp_cooling)

        def _accelerated_cpp_cooling(self):
            """start accelerated pre-cooling"""
            self.greenlet(self._accelerated_pre_csp)

        def _accelerated_pre_csp(self):
            """set cooling temp set point"""
            _log.debug("Accelerated pre-cooling for CPP Event")
            try:
                voltron_data = self._get_new_data()
            except green.Timeout:
                self._time_out()
            csp_now = float(voltron_data["ReturnAirCO2Stpt"])
            csp = csp_now - self.accel_slope
            if csp_now > csp_pre and not csp < csp_pre:
                try:
                    self._command_equip("ReturnAirCO2Stpt", csp)
                except green.Timeout:
                    self._time_out()
            elif csp_now <= csp_pre or csp < csp_pre:
                try:
                    self._command_equip("ReturnAirCO2Stpt", settings.csp_pre)
                except green.Timeout:
                    self._time_out()
                self.pre_timer.cancel()

        def _during_cpp_timer(self):
            """during CPP scheduled in get_signal"""
            self.greenlet(self._during_cpp)

        def _during_cpp(self):
            """start CPP procedure"""
            # remove when done testing
            _log.debug("During CPP Event")
            damper_cpp = settings.damper_cpp
            fan_reduction = settings.fan_reduction
            csp_cpp = settings.csp_cpp
            cpp_fan1 = self.fan1_norm - self.fan1_norm * fan_reduction
            cpp_fan2 = self.fan2_norm - self.fan2_norm * fan_reduction
            try:
                self._command_equip("CoolSupplyFanSpeed1", cpp_fan1)
                self._command_equip("CoolSupplyFanSpeed2", cpp_fan2)
                self._command_equip("ReturnAirCO2Stpt", csp_cpp)
                self._command_equip('ESMDamperMinPosition', damper_cpp)
            except green.Timeout:
                self._time_out()

        def _after_cpp_timer(self):
            """after CPP scheduled in get_signal"""
            self.greenlet(self._restore_fan_damper)
            _log.debug("After CPP Event, returning to normal operations")
            self.greenlet(self._restore_cooling_setpoint)
            timer = settings.after_time
            self.after_timer = self.periodic_timer(timer,
                                                   self._after_cpp_cooling)

        def _after_cpp_cooling(self):
            """Start after CPP procedure"""
            _log.debug("After_CPP_COOLING")
            self.greenlet(self._restore_cooling_setpoint)

        def _restore_fan_damper(self):
            """restore original fan speeds"""
            try:
                self._command_equip("ESMDamperMinPosition", self.min_damper)
                self._command_equip("CoolSupplyFanSpeed1", self.fan1_norm)
                self._command_equip("CoolSupplyFanSpeed2", self.fan2_norm)
            except green.Timeout:
                self._time_out()

        def _restore_cooling_setpoint(self):
            """restore normal cooling temp setpoint"""
            try:
                voltron_data = self._get_new_data()
            except green.Timeout:
                self._time_out()
            csp_now = float(voltron_data["ReturnAirCO2Stpt"])
            if csp_now > self.csp_norm:
                csp = csp_now - self.cooling_slope
                try:
                    self._command_equip("ReturnAirCO2Stpt", csp)
                except green.Timeout:
                    self._time_out()
            elif csp_now <= self.csp_norm:
                self.after_timer.cancel()
                try:
                    self._command_equip("ReturnAirCO2Stpt", self.csp_norm)
                    self._sleep(60)
                    self.running = False
                except green.Timeout:
                    self._time_out()

        def periodic_timer(self, *args, **kwargs):
            timer = super(Agent, self).periodic_timer(*args, **kwargs)
            self.timers.append(timer)
            return timer

        def schedule(self, time, event):
            super(Agent, self).schedule(time, event)
            self.timers.append(event)

        def greenlet(self, *args, **kwargs):
            task = greenlet.greenlet(*args, **kwargs)
            self.tasks.append(task)
            current = greenlet.getcurrent()
            if current.parent is not None:
                task.parent = current.parent
            task.switch()

        def get_signal(self):
            """get and format DR signal and schedule DR proc."""
            time_now = time.mktime(datetime.datetime.now().timetuple())
            time_pre = time.mktime(
                datetime.datetime.now().replace(hour=settings.pre_cpp_hour,
                                                minute=0,
                                                second=0,
                                                microsecond=0).timetuple())
            time_event = time.mktime(datetime.datetime.now().replace(
                hour=settings.during_cpp_hour,
                minute=12,
                second=0,
                microsecond=0).timetuple())
            time_after = time.mktime(datetime.datetime.now().replace(
                hour=settings.after_cpp_hour,
                minute=14,
                second=0,
                microsecond=0).timetuple())

            if (settings.signal and time_now < time_pre):
                _log.debug("Scheduling1")
                time_step = settings.pre_time / 3600
                #self.cooling_slope = (self.csp_norm - settings.csp_pre) / ((((time_event - time_pre) / 3600) - 0.5) * time_step)
                self.cooling_slope = 1  # for testing use a constant
                temp = ((time_event - time_pre) / 3600)
                _log.debug("cooling slope: " + repr(self.cooling_slope))
                self.schedule(time_pre, sched.Event(self._pre_cpp_timer))
                self.schedule(time_event, sched.Event(self._during_cpp_timer))
                after_cpp_time = datetime.datetime.now().replace(
                    hour=settings.after_cpp_hour,
                    minute=59,
                    second=0,
                    microsecond=0)
                self.schedule(time_after, sched.Event(self._after_cpp_timer))
                #self.start_timer.cancel()
            elif (settings.signal and time_now > time_pre
                  and time_now < time_event):
                _log.debug("Scheduling2")
                #self.start_timer.cancel()
                #self.accel_slope = (self.csp_norm - settings.csp_pre) / ((time_event - time_now) / (3600))
                self.accel_slope = 2  #for testing use a constant
                #self.cooling_slope = (self.csp_norm - settings.csp_pre) / ((((time_event - time_pre) / 3600) - 0.5) * time_step)
                self.cooling_slope = 1  # for testing use a constant
                self.schedule(time_event, sched.Event(self._during_cpp_timer))
                self.schedule(time_after, sched.Event(self._after_cpp_timer))
                self._accelerated_pre_cpp_timer()
            elif (settings.signal and time_now > time_event
                  and time_now < time_after):
                _log.debug("Too late to pre-cool!")
                #self.start_timer.cancel()
                self.schedule(time_after, sched.Event(self._after_cpp_timer))
                self._during_cpp()
            else:
                _log.debug("CPP Event Is Over")
                #self.start_timer.cancel()
                self._sleep(60)
                self.get_signal()

    Agent.__name__ = 'DemandResponseAgent'
    return Agent(**kwargs)
Esempio n. 14
0
def WeatherAgent(config_path, **kwargs):
    config = utils.load_config(config_path)

    def get_config(name):
        try:
            value = kwargs.pop(name)
        except KeyError:
            return config.get(name, '')

    agent_id = get_config('agentid')
    poll_time = get_config('poll_time')
    zip_code = get_config("zip")
    key = get_config('key')

    state = ''
    country = ''
    city = ''
    region = state if state != "" else country
    city = city
    max_requests_per_day = get_config('daily_threshold')
    max_requests_per_minute = get_config('minute_threshold')
    headers = {headers_mod.FROM: agent_id}

    class Agent(PublishMixin, BaseAgent):
        """Agent for querying WeatherUndergrounds API"""

        def __init__(self, **kwargs):
            super(Agent, self).__init__(**kwargs)
            self.valid_data = False

        def setup(self):
            super(Agent, self).setup()

            self._keep_alive = True

            self.requestCounter = RequestCounter(max_requests_per_day, max_requests_per_minute)
            # TODO: get this information from configuration file instead

            baseUrl = "http://api.wunderground.com/api/" + (key if not key == '' else settings.KEY) + "/conditions/q/"

            self.requestUrl = baseUrl
            if(zip_code != ""):
                self.requestUrl += zip_code + ".json"
            elif self.region != "":
                self.requestUrl += region + "/" + city + ".json"
            else:
                # Error Need to handle this
                print "No location selected"

            #Do a one time push when we start up so we don't have to wait for the periodic
            self.timer(10, self.weather_push)

        def build_dictionary(self):
            weather_dict = {}
            for category in categories.keys():
                weather_dict[category] = {}
                weather_elements = categories[category]
                for element in weather_elements:
                    weather_dict[category][element] = self.observation[element]

            return weather_dict

        def publish_all(self):
            self.publish_subtopic(self.build_dictionary(), "weather")

        def publish_subtopic(self, publish_item, topic_prefix):
            #TODO: Update to use the new topic templates
            if type(publish_item) is dict:
                # Publish an "all" property, converting item to json

                headers[headers_mod.CONTENT_TYPE] = headers_mod.CONTENT_TYPE.JSON
                self.publish_json(topic_prefix + topic_delim + "all", headers, json.dumps(publish_item))

                # Loop over contents, call publish_subtopic on each
                for topic in publish_item.keys():
                    self.publish_subtopic(publish_item[topic], topic_prefix + topic_delim + topic)

            else:
                # Item is a scalar type, publish it as is
                headers[headers_mod.CONTENT_TYPE] = headers_mod.CONTENT_TYPE.PLAIN_TEXT
                self.publish(topic_prefix, headers, str(publish_item))

        @periodic(poll_time)
        def weather_push(self):
            self.request_data()
            if self.valid_data:
                self.publish_all()
            else:
                _log.error("Invalid data, not publishing")

        def request_data(self):
            if self.requestCounter.request_available():
                try:
                    r = requests.get(self.requestUrl)
                    r.raise_for_status()
                    parsed_json = r.json()

                    self.observation = parsed_json['current_observation']
                    self.observation = convert(self.observation)
                    self.valid_data = True
                except Exception as e:
                    _log.error(e)
                    self.valid_data = False
                #self.print_data()

            else:
                _log.warning("No requests available")

        def print_data(self):
            print "{0:*^40}".format(" ")
            for key in self.observation.keys():
                print "{0:>25}: {1}".format(key, self.observation[key])
            print "{0:*^40}".format(" ")

    Agent.__name__ = 'WeatherAgent'
    return Agent(**kwargs)
Esempio n. 15
0
def AFDDAgent(config_path, **kwargs):
    config = utils.load_config(config_path)
    agent_id = config['agentid']
    rtu_path = dict((key, config[key])
                    for key in ['campus', 'building', 'unit'])

    class Agent(PublishMixin, BaseAgent):
        def __init__(self, **kwargs):
            super(Agent, self).__init__(**kwargs)
            self.lock_timer = None
            self.lock_acquired = False
            self.tasklet = None
            self.data_queue = green.WaitQueue(self.timer)
            self.value_queue = green.WaitQueue(self.timer)

        def setup(self):
            super(Agent, self).setup()
            headers = {
                    'Content-Type': 'text/plain',
                    'requesterID': agent_id,
            }
            self.lock_timer = self.periodic_timer(1, self.publish,
                    topics.ACTUATOR_LOCK_ACQUIRE(**rtu_path), headers)

        def start(self, algo=None):
            if algo is None:
                algo = afdd
            self.tasklet = greenlet.greenlet(algo)
            self.tasklet.switch(self)

        @matching.match_exact(topics.ACTUATOR_LOCK_ACQUIRE(**rtu_path))
        def on_lock_sent(self, topic, headers, message, match):
            self.lock_timer.cancel()

        @matching.match_exact(topics.ACTUATOR_LOCK_RESULT(**rtu_path))
        def on_lock_result(self, topic, headers, message, match):
            msg = jsonapi.loads(message[0])
            holding_lock = self.lock_acquired
            if headers['requesterID'] == agent_id:
                self.lock_acquired = msg == 'SUCCESS'
            elif msg == 'SUCCESS':
                self.lock_acquired = False
            if self.lock_acquired and not holding_lock:
                self.start()

        @matching.match_exact(topics.RTU_VALUE(point='all', **rtu_path))
        def on_new_data(self, topic, headers, message, match):
            data = jsonapi.loads(message[0])
            self.data_queue.notify_all(data)
    
        @matching.match_glob(topics.ACTUATOR_VALUE(point='*', **rtu_path))
        def on_set_result(self, topic, headers, message, match):
            self.value_queue.notify_all((match.group(1), True))
    
        @matching.match_glob(topics.ACTUATOR_ERROR(point='*', **rtu_path))
        def on_set_error(self, topic, headers, message, match):
            self.value_queue.notify_all((match.group(1), False))

        def sleep(self, timeout):
            _log.debug('sleep({})'.format(timeout))
            green.sleep(timeout, self.timer)

        def get_new_data(self, timeout=None):
            _log.debug('get_new_data({})'.format(timeout))
            return self.data_queue.wait(timeout)

        def set_point(self, point_name, value, timeout=None):
            _log.debug('set_point({}, {}, {})'.format(point_name, value, timeout))
            headers = {
                    'Content-Type': 'text/plain',
                    'requesterID': agent_id,
            }
            self.publish(topics.ACTUATOR_SET(point=point_name, **rtu_path),
                         headers, str(value))
            try:
                return self.value_queue.wait(timeout)
            except green.Timeout:
                return None

    Agent.__name__ = 'AFDDAgent'
    return Agent(**kwargs)
Esempio n. 16
0
def AFDDAgent(config_path, **kwargs):
    publish_address = kwargs['publish_address']
    config = utils.load_config(config_path)
    agent_id = config['agentid']
    rtu_path = dict(
        (key, config[key]) for key in ['campus', 'building', 'unit'])

    class Agent(PublishMixin, BaseAgent):
        def __init__(self, **kwargs):
            super(Agent, self).__init__(**kwargs)
            self.lock_acquired = False
            self.thread = None
            self.data_queue = multithreading.WaitQueue()
            self.value_queue = multithreading.WaitQueue()

        def setup(self):
            super(Agent, self).setup()
            headers = {
                'Content-Type': 'text/plain',
                'requesterID': agent_id,
            }
            self.publish(topics.ACTUATOR_LOCK_ACQUIRE(**rtu_path), headers)

        def start(self, algo=None):
            if algo is None:
                algo = afdd

            def run():
                sock = messaging.Socket(zmq.PUSH)
                sock.connect(publish_address)
                with contextlib.closing(sock):
                    algo(self, sock)

            self.thread = threading.Thread(target=run)
            self.thread.daemon = True
            self.thread.start()

        @matching.match_exact(topics.ACTUATOR_LOCK_RESULT(**rtu_path))
        def on_lock_result(self, topic, headers, message, match):
            msg = jsonapi.loads(message[0])
            holding_lock = self.lock_acquired
            if headers['requesterID'] == agent_id:
                self.lock_acquired = msg == 'SUCCESS'
            elif msg == 'SUCCESS':
                self.lock_acquired = False
            if self.lock_acquired and not holding_lock:
                self.start()

        @matching.match_exact(topics.RTU_VALUE(point='all', **rtu_path))
        def on_new_data(self, topic, headers, message, match):
            data = jsonapi.loads(message[0])
            self.data_queue.notify_all(data)

        @matching.match_glob(topics.ACTUATOR_VALUE(point='*', **rtu_path))
        def on_set_result(self, topic, headers, message, match):
            self.value_queue.notify_all((match.group(1), True))

        @matching.match_glob(topics.ACTUATOR_ERROR(point='*', **rtu_path))
        def on_set_error(self, topic, headers, message, match):
            self.value_queue.notify_all((match.group(1), False))

        def get_new_data(self, timeout=None):
            _log.debug('get_new_data({})'.format(timeout))
            return self.data_queue.wait(timeout)

        def set_point(self, sock, point_name, value, timeout=None):
            _log.debug('set_point({}, {}, {})'.format(point_name, value,
                                                      timeout))
            headers = {
                'Content-Type': 'text/plain',
                'requesterID': agent_id,
            }
            with self.value_queue.condition:
                sock.send_message(topics.ACTUATOR_SET(point=point_name,
                                                      **rtu_path),
                                  headers,
                                  str(value),
                                  flags=zmq.NOBLOCK)
                try:
                    return self.value_queue._wait(timeout)
                except multithreading.Timeout:
                    return None

    Agent.__name__ = 'AFDDAgent'
    return Agent(**kwargs)
Esempio n. 17
0
 def __init__(self, config_path, **kwargs):
     super(ListenerAgent, self).__init__(**kwargs)
     self.config = utils.load_config(config_path)
Esempio n. 18
0
def DemandResponseAgent(config_path, **kwargs):
    """DR application for time of use pricing"""
    config = utils.load_config(config_path)
    agent_id = config['agentid']
    rtu_path = dict((key, config[key])
                    for key in ['campus', 'building', 'unit'])
    schedule = config.get('Schedule')
    datefmt = '%Y-%m-%d %H:%M:%S'

    csp_pre = config.get('csp_pre', 67.0)
    csp_cpp = config.get('csp_cpp', 80.0)
    damper_cpp = config.get('damper_cpp', 0.0)
    fan_reduction = config.get('fan_reduction', 0.1)
    max_precool_hours = config.get('max_precool_hours', 5)
    cooling_stage_differential = config.get('cooling_stage_differential', 1.0)
    cpp_end_hour = config.get('cpp_end_hour', 18)
    cpp_end_minute = config.get('cpp_end_minute', 0) 
    timestep_length = config.get('timestep_length', 900)
    building_thermal_constant = config.get('building_thermal_constant', 4.0)
    
    #point names for controller 
    cooling_stpt = config.get('cooling_stpt')
    heating_stpt = config.get('heating_stpt')
    min_damper_stpt = config.get('min_damper_stpt')
    cooling_stage_diff = config.get('cooling_stage_diff')
    cooling_fan_sp1 = config.get('cooling_fan_sp1')
    cooling_fan_sp2 = config.get('cooling_fan_sp2')
    override_command = config.get('override_command')
    occupied_status = config.get('occupied_status')
    space_temp = config.get('space_temp')
    volttron_flag = config.get('volttron_flag')
    log_filename = config.get('file_name')
    
    debug_flag = False
    if not debug_flag:
        _log = logging.getLogger(__name__)
        logging.basicConfig(level=logging.DEBUG, stream=sys.stderr,
                            format='%(asctime)s   %(levelname)-8s %(message)s',
                            datefmt='%Y-%m-%d %H:%M:%S')
    else:
        _log = logging.getLogger(__name__)
        logging.basicConfig(level=logging.NOTSET, stream=sys.stderr,
                        format='%(asctime)s   %(levelname)-8s %(message)s',
                        datefmt='%Y-%m-%d %H:%M:%S',
                        filename=log_filename,
                        filemode='a+')
        fmt_str = '%(asctime)s   %(levelname)-8s    %(message)s'
        formatter = logging.Formatter(fmt_str, datefmt = '%Y-%m-%d %H:%M:%S')
        console = logging.StreamHandler()
        console.setLevel(logging.DEBUG)
        console.setFormatter(formatter)
        logging.getLogger("").addHandler(console)
    
    class Agent(PublishMixin, BaseAgent):
        """Class agent"""

        def __init__(self, **kwargs):
            super(Agent, self).__init__(**kwargs)
            
            self.normal_firststage_fanspeed = config.get('normal_firststage_fanspeed', 75.0)
            self.normal_secondstage_fanspeed = config.get('normal_secondstage_fanspeed', 90.0)
            self.normal_damper_stpt = config.get('normal_damper_stpt', 5.0)
            self.normal_coolingstpt = config.get('normal_coolingstpt', 74.0)
            self.normal_heatingstpt = config.get('normal_heatingstpt', 67.0)
            self.smap_path = config.get('smap_path')
            self.default_cooling_stage_differential  = 0.5
            self.current_spacetemp = 0.0
            
            self.state = 'STARTUP'
            self.e_start_msg = None
            self.lock_handler = None
            self.error_handler = None
            self.actuator_handler = None
            self.pre_cool_idle = None
            self.e_start = None
            self.e_end = None
            self.pre_stored_spacetemp =None
            
            self.all_scheduled_events = {}
            self.currently_running_dr_event_handlers = []
            self.headers = {headers_mod.CONTENT_TYPE: headers_mod.CONTENT_TYPE.JSON, 'requesterID': agent_id}
            
        @matching.match_headers({headers_mod.REQUESTER_ID: agent_id}) 
        @matching.match_exact(topics.ACTUATOR_LOCK_RESULT(**rtu_path))
        def _on_lock_result(self, topic, headers, message, match):
            """lock result"""
            msg = jsonapi.loads(message[0])
            _log.debug('Lock Result:  ' + str(headers.get('requesterID', '')) + '   ' + str(msg))
            if msg == 'SUCCESS' and self.lock_handler is not None:
                self.lock_handler()
            if msg == 'FAILURE' and self.error_handler is not None:
                self.error_handler()
                   
        @matching.match_headers({headers_mod.REQUESTER_ID: agent_id}) 
        @matching.match_glob(topics.ACTUATOR_ERROR(point='*', **rtu_path))
        def _on_error_result(self, topic, headers, message, match):
            """lock result"""
            point = match.group(1)
            msg = jsonapi.loads(message[0])
            point = match.group(1)
            _log.debug('Lock Error Results: '+str(point) + '  '+ str(msg))  
            if self.error_handler is not None:
                _log.debug('Running lock error handler')
                self.error_handler()
                
        @matching.match_headers({headers_mod.REQUESTER_ID: agent_id})                 
        @matching.match_glob(topics.ACTUATOR_VALUE(point='*', **rtu_path))
        def _on_actuator_result(self, topic, headers, message, match):
            """lock result"""
            msg = jsonapi.loads(message[0])
            point = match.group(1)
            if point != 'PlatformHeartBeat':
                _log.debug('Actuator Results:  ' + str(point) +'  ' + str(msg))
            if self.actuator_handler is not None:
                _log.debug('Running Actuator Handler')
                self.actuator_handler(point, jsonapi.loads(message[0]))

        @matching.match_exact(topics.RTU_VALUE(point='all', **rtu_path))
        def _on_new_data(self, topic, headers, message, match):
            """watching for new data"""
            data = jsonapi.loads(message[0])
            self.current_spacetemp = float(data[space_temp])
            dr_override = bool(int(data[override_command]))
            occupied = bool(int(data[occupied_status]))
            
            if dr_override and self.state not in ('IDLE', 'CLEANUP', 'STARTUP'):
                _log.debug('User Override Initiated')
                self.cancel_event(cancel_type='OVERRIDE')
            
            if not occupied and self.state in ('DR_EVENT', 'RESTORE'):
                self.cancel_event()
 
            if self.state == 'STARTUP':
                _log.debug('Finished Startup')
                self.state = 'IDLE'
                
        @matching.match_exact(topics.OPENADR_EVENT())
        def _on_dr_event(self, topic, headers, message, match):
            if self.state == 'STARTUP':
                _log.debug('DR event ignored because of startup.')
                return
            """handle openADR events"""
            msg = jsonapi.loads(message[0])
            _log.debug('EVENT Received:  ' + str(msg))
            e_id = msg['id']
            e_status = msg['status']
            e_start = msg['start_at']
            #e_start = datetime.datetime.strptime(e_start, datefmt)
            today = datetime.datetime.now().date()
            e_end = msg['end_at']
            e_end = parser.parse(e_end, fuzzy=True)
            e_start = parser.parse(e_start, fuzzy=True)
            #e_end = datetime.datetime.strptime(e_end, datefmt)
            current_datetime = datetime.datetime.now()
            
            #For UTC offset
            #offset= datetime.datetime.utcnow() - datetime.datetime.now()
            #e_end = e_end - offset
            #e_start = e_start - offset
            
            if current_datetime > e_end:
                _log.debug('Too Late Event is Over')
                return
            
            if e_status == 'cancelled':
                if e_start in self.all_scheduled_events:
                    _log.debug('Event Cancelled')
                    self.all_scheduled_events[e_start].cancel()
                    del self.all_scheduled_events[e_start]
                    
                if e_start.date() == today and (self.state == 'PRECOOL' or self.state == 'DR_EVENT'):
                    self.cancel_event()
                return

            if today > e_start.date():
                if e_start in self.all_scheduled_events:
                    self.all_scheduled_events[e_start].cancel()
                    del self.all_scheduled_events[e_start]
                return
            
            for item in self.all_scheduled_events.keys():
                if e_start.date() == item.date():
                    if e_start.time() != item.time():
                        _log.debug( 'Updating Event')
                        self.all_scheduled_events[item].cancel()
                        del self.all_scheduled_events[item]
                        if e_start.date() == today and (self.state == 'PRECOOL' or self.state == 'DR_EVENT'):
                            self.cancel_event(cancel_type='UPDATING')
                        break
                    elif e_start.time() == item.time():
                        _log.debug("same event")
                        return
            
            #Don't schedule an event if we are currently in OVERRIDE state.    
            if e_start.date() == today and (self.state == 'OVERRIDE'):
                return
            self.e_start = e_start
            self.e_end = e_end
            event_start = e_start - datetime.timedelta(hours = max_precool_hours)  
    
            event = sched.Event(self.pre_cool_get_lock, args=[self.e_start, self.e_end])
            self.schedule(event_start, event) 
            self.all_scheduled_events[e_start] = event                
                
        def pre_cool_get_lock(self, e_start, e_end):
            if self.state == 'OVERRIDE':
                _log.debug("Override today")
                return
            
            if self.pre_cool_idle == False:
                return
            
            now = datetime.datetime.now()
            day=now.weekday()

            if not schedule[day]:
                _log.debug("Unoccupied today")
                return
            
            if self.state == 'PRECOOL' and self.pre_cool_idle == True:
                for event in self.currently_running_dr_event_handlers:
                    event.cancel()
                    self.all_scheduled_events = {}
                    
            self.state = 'PRECOOL'
            e_start_unix = time.mktime(e_start.timetuple())
            e_end_unix = time.mktime(e_end.timetuple())
    
            if self.pre_cool_idle == True:
                event_start = now + datetime.timedelta(minutes=15)   
                event = sched.Event(self.pre_cool_get_lock, args=[self.e_start, self.e_end])
                self.schedule(event_start, event) 
                self.all_scheduled_events[e_start] = event 
                self.pre_cool_idle = True
                self.schedule_builder(e_start_unix, e_end_unix,
                                      current_spacetemp=self.current_spacetemp,
                                      pre_csp=csp_pre,
                                      building_thermal_constant=building_thermal_constant,
                                      normal_coolingstpt=self.normal_coolingstpt,
                                      timestep_length=timestep_length,
                                      dr_csp=csp_cpp)
            else:
                event_start = now + datetime.timedelta(minutes=15)   
                event = sched.Event(self.pre_cool_get_lock, args=[self.e_start, self.e_end])
                self.schedule(event_start, event) 
                self.all_scheduled_events[e_start] = event 
                self.pre_cool_idle = True
                def run_schedule_builder():
                    self.schedule_builder(e_start_unix, e_end_unix,
                                          current_spacetemp=self.current_spacetemp,
                                          pre_csp=csp_pre,
                                          building_thermal_constant=building_thermal_constant,
                                          normal_coolingstpt=self.normal_coolingstpt,
                                          timestep_length=timestep_length,
                                          dr_csp=csp_cpp)
                    self.lock_handler=None
                
                self.lock_handler = run_schedule_builder
                
                headers = {
                    headers_mod.CONTENT_TYPE: headers_mod.CONTENT_TYPE.JSON,
                    'requesterID': agent_id}
                self.publish(topics.ACTUATOR_LOCK_ACQUIRE(**rtu_path), headers)
                
                def retry_lock():
                    def retry_lock_event():
                        headers = {
                                   headers_mod.CONTENT_TYPE: headers_mod.CONTENT_TYPE.JSON,
                                   'requesterID': agent_id}
                        self.publish(topics.ACTUATOR_LOCK_ACQUIRE(**rtu_path), headers)
                        
                    retry_time = datetime.datetime.now() + datetime.timedelta(seconds=5)
                    if retry_time > e_end:
                        self.state = 'IDLE'
                        self.error_handler = None
                        return
                    
                    event = sched.Event(retry_lock_event)
                    self.schedule(retry_time, event) 
                
                self.error_handler = retry_lock
            
        def modify_temp_set_point(self, csp, hsp):
            self.publish(topics.ACTUATOR_SET(point=volttron_flag, **rtu_path), self.headers, str(3.0))
            self.publish(topics.ACTUATOR_SET(point=min_damper_stpt, **rtu_path), self.headers, str(self.normal_damper_stpt))
            self.publish(topics.ACTUATOR_SET(point=cooling_stage_diff, **rtu_path), self.headers, str(self.default_cooling_stage_differential))
            self.publish(topics.ACTUATOR_SET(point=cooling_stpt, **rtu_path), self.headers, str(csp))
            self.publish(topics.ACTUATOR_SET(point=heating_stpt, **rtu_path), self.headers, str(hsp))
           
            if self.pre_cool_idle == True:
                self.pre_cool_idle = False
            
            def backup_run():
                self.modify_temp_set_point(csp, hsp)
                self.lock_handler=None
                
            self.lock_handler = backup_run
            
        def start_dr_event(self):
            self.state = 'DR_EVENT'
            self.publish(topics.ACTUATOR_SET(point=volttron_flag, **rtu_path), self.headers, str(3))
            self.publish(topics.ACTUATOR_SET(point=cooling_stpt, **rtu_path), self.headers, str(csp_cpp))
            
            new_fan_speed = self.normal_firststage_fanspeed - (self.normal_firststage_fanspeed*fan_reduction)
            new_fan_speed = max(new_fan_speed,0)
            self.publish(topics.ACTUATOR_SET(point=cooling_fan_sp1, **rtu_path), self.headers, str(new_fan_speed))
            
            new_fan_speed = self.normal_secondstage_fanspeed - (self.normal_firststage_fanspeed*fan_reduction)
            new_fan_speed = max(new_fan_speed,0)
            self.publish(topics.ACTUATOR_SET(point=cooling_fan_sp2, **rtu_path), self.headers, str(new_fan_speed))            
            
            self.publish(topics.ACTUATOR_SET(point=min_damper_stpt, **rtu_path), self.headers, str(damper_cpp))
            self.publish(topics.ACTUATOR_SET(point=cooling_stage_diff, **rtu_path), self.headers, str(cooling_stage_differential))
            mytime = int(time.time())
            content = {
                "Demand Response Event": {
                     "Readings": [[mytime, 1.0]],
                     "Units": "TU",
                     "data_type": "double"
                 }
            }
            self.publish(self.smap_path, self.headers, jsonapi.dumps(content))    
            def backup_run():
                self.start_dr_event()
                self.lock_handler = None
                
            self.lock_handler = backup_run
            
        def start_restore_event(self, csp, hsp):
            self.state = 'RESTORE'
            _log.debug('Restore:  Begin restoring normal operations')
            self.publish(topics.ACTUATOR_SET(point=cooling_stpt, **rtu_path), self.headers, str(csp))
            self.publish(topics.ACTUATOR_SET(point=heating_stpt, **rtu_path), self.headers, str(hsp)) #heating
            self.publish(topics.ACTUATOR_SET(point=cooling_fan_sp1, **rtu_path), self.headers, str(self.normal_firststage_fanspeed))
            self.publish(topics.ACTUATOR_SET(point=cooling_fan_sp2, **rtu_path), self.headers, str(self.normal_secondstage_fanspeed))            
            
            self.publish(topics.ACTUATOR_SET(point=min_damper_stpt, **rtu_path), self.headers, str(self.normal_damper_stpt))
            self.publish(topics.ACTUATOR_SET(point=cooling_stage_diff, **rtu_path), self.headers, str(self.default_cooling_stage_differential))
                
            def backup_run():
                self.start_restore_event(csp, hsp)
                self.lock_handler=None
                
            self.lock_handler = backup_run
            
        def cancel_event(self, cancel_type='NORMAL'):
            if cancel_type == 'OVERRIDE':
                self.state = 'OVERRIDE'
                smap_input = 3.0
            elif cancel_type != 'UPDATING':
                self.state = 'CLEANUP'
                smap_input = 2.0
    
                            
            self.publish(topics.ACTUATOR_SET(point=cooling_stpt, **rtu_path), self.headers, str(self.normal_coolingstpt))
            self.publish(topics.ACTUATOR_SET(point=heating_stpt, **rtu_path), self.headers, str(self.normal_heatingstpt))
            self.publish(topics.ACTUATOR_SET(point=cooling_fan_sp1, **rtu_path), self.headers, str(self.normal_firststage_fanspeed))
            self.publish(topics.ACTUATOR_SET(point=cooling_fan_sp2, **rtu_path), self.headers, str(self.normal_secondstage_fanspeed))            
            
            self.publish(topics.ACTUATOR_SET(point=min_damper_stpt, **rtu_path), self.headers, str(self.normal_damper_stpt))
            self.publish(topics.ACTUATOR_SET(point=cooling_stage_diff, **rtu_path), self.headers, str(self.default_cooling_stage_differential))
            self.publish(topics.ACTUATOR_SET(point=volttron_flag, **rtu_path), self.headers,str( 0))
            
            for event in self.currently_running_dr_event_handlers:
                event.cancel()
                
            if cancel_type != 'UPDATING':
                mytime = int(time.time())
                content = {
                    "Demand Response Event": {
                         "Readings": [[mytime, smap_input]],
                         "Units": "TU",
                         "data_type": "double"
                     }
                }
                self.publish(self.smap_path, self.headers, jsonapi.dumps(content))
                    
            self.currently_running_dr_event_handlers = []
            def backup_run():
                self.cancel_event()
                self.lock_handler=None
                
            self.lock_handler = backup_run
            
            expected_values = {cooling_stpt: self.normal_coolingstpt,
                               heating_stpt: self.normal_heatingstpt,
                               cooling_fan_sp1: self.normal_firststage_fanspeed,
                               cooling_fan_sp2: self.normal_secondstage_fanspeed,
                               min_damper_stpt: self.normal_damper_stpt,
                               cooling_stage_diff: self.default_cooling_stage_differential}
            
            EPSILON = 0.5  #allowed difference from expected value
            
            def result_handler(point, value):
                #print "actuator point being handled:", point, value
                expected_value = expected_values.pop(point, None)
                if expected_value is not None:
                    diff = abs(expected_value-value)
                    if diff > EPSILON:
                        _log.debug( "Did not get back expected value for:  " + str(point))
                        
                if not expected_values:
                    self.actuator_handler = None
                    self.lock_handler=None
                    self.error_handler = None
                    self.state = 'IDLE' if not cancel_type == 'OVERRIDE' else 'OVERRIDE'
                    
                    headers = {
                        headers_mod.CONTENT_TYPE: headers_mod.CONTENT_TYPE.JSON,
                        'requesterID': agent_id}
                    self.publish(topics.ACTUATOR_LOCK_RELEASE(**rtu_path), headers)
            
            if cancel_type != 'UPDATING':
                self.actuator_handler = result_handler
            else:
                self.actuator_handler = None
                self.lock_handler=None
            
            if cancel_type == 'OVERRIDE':
                def on_reset():
                    self.error_handler = None
                    self.state = 'IDLE'    
                    
                today = datetime.datetime.now()
                reset_time = today + datetime.timedelta(days=1)
                reset_time = reset_time.replace(hour=0, minute =0, second = 0)             
                
                event = sched.Event(on_reset)
                self.schedule(reset_time, event) 
          
        def schedule_builder(self,start_time, end_time, 
                             current_spacetemp,
                             pre_csp,
                             building_thermal_constant,
                             normal_coolingstpt,
                             timestep_length,
                             dr_csp):
            """schedule all events for a DR event."""
            current_time = time.time()
            if current_time > end_time:
                return

            _log.debug('Scheduling all DR actions')  
            pre_hsp = pre_csp - 5.0

            ideal_cooling_window = int(((current_spacetemp - pre_csp)/building_thermal_constant) *3600)  
            ideal_precool_start_time = start_time - ideal_cooling_window
            
            max_cooling_window = start_time - current_time
            
            cooling_window = ideal_cooling_window if ideal_cooling_window < max_cooling_window else max_cooling_window
            
            precool_start_time = start_time - cooling_window
            pre_cool_step = 0
            if (max_cooling_window > 0):
                _log.debug('Schedule Pre Cooling')
                num_cooling_timesteps = int(math.ceil(float(cooling_window) / float(timestep_length)))         
                cooling_step_delta = (normal_coolingstpt - pre_csp) / num_cooling_timesteps
                
                if num_cooling_timesteps <= 0:
                    num_cooling_timesteps=1
                    
                for step_index in range (1, num_cooling_timesteps):
                    if step_index == 1:
                        pre_cool_step = 2*timestep_length
                    else:
                        pre_cool_step += timestep_length
                        
                    event_time = start_time - pre_cool_step
                    csp = pre_csp + ((step_index-1) * cooling_step_delta)
                    
                    _log.debug('Precool step:  '+ str(datetime.datetime.fromtimestamp(event_time)) + '   CSP:  ' + str(csp))
                    event = sched.Event(self.modify_temp_set_point, args = [csp, pre_hsp])
                    self.schedule(event_time, event)
                    self.currently_running_dr_event_handlers.append(event)
            
            else:
                _log.debug('Too late to pre-cool!')
            
            restore_window = int(((dr_csp - normal_coolingstpt)/building_thermal_constant) *3600)  
            restore_start_time = end_time
            num_restore_timesteps = int(math.ceil(float(restore_window) / float(timestep_length)))         
            restore_step_delta = (dr_csp - normal_coolingstpt) / num_restore_timesteps
                
            _log.debug('Schedule DR Event: ' + str(datetime.datetime.fromtimestamp(start_time)) +'   CSP:  ' + str(dr_csp))
            event = sched.Event(self.start_dr_event)
            self.schedule(start_time, event)
            self.currently_running_dr_event_handlers.append(event)
            
            _log.debug('Schedule Restore Event:  '+ str(datetime.datetime.fromtimestamp(end_time)) + '   CSP:  ' + str(dr_csp-restore_step_delta))
            event = sched.Event(self.start_restore_event, args = [dr_csp-restore_step_delta, self.normal_heatingstpt])
            self.schedule(end_time, event)
            self.currently_running_dr_event_handlers.append(event)
                
            for step_index in range (1, num_restore_timesteps):
                event_time = end_time + (step_index * timestep_length)
                csp = dr_csp - ((step_index + 1) * restore_step_delta)
                
                _log.debug('Restore step: ' + str(datetime.datetime.fromtimestamp(event_time)) +'   CSP:  ' + str(csp))
                event = sched.Event(self.modify_temp_set_point, args = [csp, self.normal_heatingstpt])
                self.schedule(event_time, event)
                self.currently_running_dr_event_handlers.append(event)
            
            event_time = end_time + (num_restore_timesteps * timestep_length)
            _log.debug('Schedule Cleanup Event:  ' + str(datetime.datetime.fromtimestamp(event_time)))
            event = sched.Event(self.cancel_event)
            self.schedule(event_time,event)
            self.currently_running_dr_event_handlers.append(event)
               
    Agent.__name__ = 'DemandResponseAgent'
    return Agent(**kwargs) 
Esempio n. 19
0
 def __init__(self, config_path, **kwargs):
     super(IEBSubscriber, self).__init__(**kwargs)
     self.config = utils.load_config(config_path)
Esempio n. 20
0
def DeviceDiscoveryAgent(config_path, **kwargs):
    config = utils.load_config(
        config_path
    )  # load the config_path from devicediscoveryagent.launch.json

    def get_config(name):
        try:
            value = kwargs.pop(
                name)  # from the **kwargs when call this function
        except KeyError:
            return config.get(name, '')

    #1. @params agent
    agent_id = get_config('agent_id')
    device_scan_time = get_config('device_scan_time')
    device_scan_time_multiplier = get_config('device_scan_time_multiplier')
    #device_scan_time_multiplier=1
    headers = {headers_mod.FROM: agent_id}
    publish_address = 'ipc:///tmp/volttron-lite-agent-publish'
    subscribe_address = 'ipc:///tmp/volttron-lite-agent-subscribe'
    topic_delim = '/'  # topic delimiter

    #3. @params agent & DB interfaces
    #@params DB interfaces
    db_database = settings.DATABASES['default']['NAME']
    db_host = settings.DATABASES['default']['HOST']
    db_port = settings.DATABASES['default']['PORT']
    db_user = settings.DATABASES['default']['USER']
    db_password = settings.DATABASES['default']['PASSWORD']
    db_table_device_info = settings.DATABASES['default']['TABLE_device_info']
    #db_table_dashboard_current_status = settings.DATABASES['default']['TABLE_dashboard_current_status']
    db_table_supported_devices = settings.DATABASES['default'][
        'TABLE_supported_devices']

    #4. @params dummy devicediscovery agent setting
    dummy_device_discovery = settings.DUMMY_SETTINGS['dummy_discovery']
    num_of_dummy_hvac = settings.DUMMY_SETTINGS['number_of_hvac']
    num_of_dummy_lighting = settings.DUMMY_SETTINGS['number_of_lighting']
    num_of_dummy_plugload = settings.DUMMY_SETTINGS['number_of_plugload']

    #5. @params devicediscovery agent setting
    device_monitor_time = settings.DEVICES['device_monitor_time']
    findWiFi = settings.FIND_DEVICE_SETTINGS['findWiFi']
    findWiFiHue = settings.FIND_DEVICE_SETTINGS['findWiFiHue']
    findWiFiWeMo = settings.FIND_DEVICE_SETTINGS['findWiFiWeMo']

    #@paths
    PROJECT_DIR = settings.PROJECT_DIR
    Loaded_Agents_DIR = settings.Loaded_Agents_DIR
    Autostart_Agents_DIR = settings.Autostart_Agents_DIR
    Applications_Launch_DIR = settings.Applications_Launch_DIR
    Agents_Launch_DIR = settings.Agents_Launch_DIR

    class Agent(PublishMixin, BaseAgent):
        def __init__(self, **kwargs):
            super(Agent, self).__init__(**kwargs)
            # Connect to database
            self.con = psycopg2.connect(host=db_host,
                                        port=db_port,
                                        database=db_database,
                                        user=db_user,
                                        password=db_password)
            self.cur = self.con.cursor(
            )  # open a cursor to perform database operations

            sys.path.append(PROJECT_DIR)

            self.device_scan_time = device_scan_time
            self.device_discovery_start_time = datetime.datetime.now()
            self.scan_for_devices = True

            self.findWiFi = findWiFi
            self.findWiFiHue = findWiFiHue
            self.findWiFiWeMo = findWiFiWeMo

            self.dummy_device_discovery = dummy_device_discovery
            if self.dummy_device_discovery:
                self.dummy_device_types = {
                    'hvac': {
                        'thermostat': 'thermostat'
                    },
                    'lighting': {
                        'Philips': 'lighting',
                        'WeMo_lighting': 'lighting'
                    },
                    'plugload': {
                        'WeMo_plugload': 'plugload'
                    }
                }
                self.num_of_dummy_devices = dict()
                self.num_of_dummy_devices['hvac'] = num_of_dummy_hvac
                self.num_of_dummy_devices['lighting'] = num_of_dummy_lighting
                self.num_of_dummy_devices['plugload'] = num_of_dummy_plugload

            self.new_discovery = True
            self.no_new_discovery_count = 0

            try:
                # Find total number of devices in the dashboard_device_info table
                self.cur.execute("SELECT * FROM " + db_table_device_info)
                self.device_num = self.cur.rowcount  # count no. of devices discovered by Device Discovery Agent
                print "{} >> there are existing {} device(s) in database".format(
                    agent_id, self.device_num)
                #if self.device_num != 0:  # change network status of devices to OFF (OFFLINE)
                #    rows = self.cur.fetchall()
                #    for row in rows:
                #        self.cur.execute("UPDATE "+db_table_device_info+" SET device_status=%s", ("OFF",))
                #        self.con.commit()
            except:
                self.device_num = 0

        def setup(self):
            super(Agent, self).setup()
            self.valid_data = False
            '''Discovery Processes'''
            while True:
                if self.scan_for_devices:
                    self.deviceScannerBehavior()
                    if not self.new_discovery:
                        self.no_new_discovery_count += 1
                    else:
                        self.no_new_discovery_count = 0
                    if self.no_new_discovery_count >= 10:
                        self.device_scan_time *= device_scan_time_multiplier
                    time.sleep(self.device_scan_time)
                else:
                    pass

        #deviceScannerBehavior (TickerBehavior)
        def deviceScannerBehavior(self):
            self.new_discovery = False
            print "Start Discovery Processes--------------------------------------------------"
            print "{} >> device next scan time in {} sec".format(
                agent_id, str(self.device_scan_time))
            print "{} >> start_time {}".format(
                agent_id, str(self.device_discovery_start_time))
            self.device_discovery_time_now = datetime.datetime.now()
            print "{} >> current time {}".format(
                agent_id, str(self.device_discovery_time_now))
            print "{} >> is trying to discover all available devices\n".format(
                agent_id)

            # Check if dummy or real device discovery:
            if self.dummy_device_discovery:
                for device_type in self.dummy_device_types.keys():
                    if self.num_of_dummy_devices[device_type] > 0:
                        num_of_devices_to_discover_now = random.randint(
                            1, self.num_of_dummy_devices[device_type])
                        type_no_of_device_to_discover_now = random.randint(
                            0,
                            len(self.dummy_device_types[device_type].keys()) -
                            1)
                        type_of_device_to_discover_now = self.dummy_device_types[
                            device_type].keys(
                            )[type_no_of_device_to_discover_now]
                        device_number = num_of_devices_to_discover_now
                        while device_number > 0:
                            device_number -= self.findDevicesbytype(
                                "Dummy", self.dummy_device_types[device_type]
                                [type_of_device_to_discover_now],
                                type_of_device_to_discover_now)
                        self.num_of_dummy_devices[
                            device_type] -= num_of_devices_to_discover_now

            else:
                # Finding devices by type:

                if self.findWiFi:
                    self.findDevicesbytype("WiFi", "thermostat", "thermostat")
                if self.findWiFiWeMo:
                    self.findDevicesbytype("WiFi", "plugload", "WeMo")
                    self.findDevicesbytype("WiFi", "lighting", "WeMo")
                if self.findWiFiHue:
                    self.findDevicesbytype("WiFi", "lighting", "Philips")
            print "Stop Discovery Processes---------------------------------------------------"

        def findDevicesbytype(self, com_type, controller_type, discovery_type):
            #******************************************************************************************************

            self.cur.execute(
                "SELECT device_type FROM " + db_table_device_info +
                " WHERE device_type=%s", (controller_type, ))
            num_Devices = self.cur.rowcount
            num_new_Devices = 0

            print "{} >> is finding available {} {} devices ...".format(
                agent_id, com_type, discovery_type)
            discovery_module = importlib.import_module("discoverAPI." +
                                                       com_type)
            discovery_returns_ip = True

            discovered_address = discovery_module.discover(discovery_type)

            print discovered_address

            for address in discovered_address:
                if discovery_returns_ip:
                    ip_address = address
                    try:
                        macaddress = discovery_module.getMACaddress(
                            discovery_type, ip_address)
                        if macaddress is not None:
                            _valid_macaddress = True
                        else:
                            _valid_macaddress = False
                    except:
                        _valid_macaddress = False

                else:
                    ip_address = None
                    macaddress = address
                    _valid_macaddress = True

                if _valid_macaddress:
                    if self.checkMACinDB(self.con, macaddress):
                        newdeviceflag = False
                        self.cur.execute(
                            "SELECT device_id from " + db_table_device_info +
                            " where mac_address=%s", (macaddress, ))
                        deviceID = self.cur.fetchone()[0]
                        agent_launch_file = deviceID + ".launch.json"
                        self.cur.execute(
                            "SELECT bemoss from " + db_table_device_info +
                            " where device_id=%s", (deviceID, ))
                        bemoss_status = self.cur.fetchone()[0]
                        if self.device_agent_still_running(agent_launch_file):
                            print "{} >> {} for device with MAC address {} is still running"\
                                    .format(agent_id, agent_launch_file, macaddress)
                            if not bemoss_status:
                                print '{} >> Device with MAC address {} found to be Non-BEMOSS, Stopping agent {}'\
                                        .format(agent_id, macaddress, agent_launch_file)
                                os.system("bin/volttron-ctrl stop-agent " +
                                          agent_launch_file)
                            # else:
                            #     self.cur.execute("UPDATE "+db_table_device_info+" SET device_status=%s where "
                            #                                                               "id=%s", ("ON", deviceID))
                            #     self.con.commit()
                        else:
                            print "{} >> {} for device with MAC address {} is not running"\
                                    .format(agent_id, agent_launch_file, macaddress)
                            #restart agent if in BEMOSS Core
                            if bemoss_status:
                                self.cur.execute(
                                    "SELECT device_type from " +
                                    db_table_device_info +
                                    " where device_id=%s", (deviceID, ))
                                stopped_agent_device_type = self.cur.fetchone(
                                )[0]
                                self.cur.execute(
                                    "SELECT zone_id from " +
                                    stopped_agent_device_type + " where " +
                                    stopped_agent_device_type + "_id=%s",
                                    (deviceID, ))
                                stopped_agent_zone_id = self.cur.fetchone()[0]
                                if stopped_agent_zone_id == 999:
                                    self.launch_agent(Agents_Launch_DIR,
                                                      agent_launch_file)
                                    print "{} >> {} has been restarted"\
                                            .format(agent_id, agent_launch_file)
                                else:
                                    print "{} >> {} is running on another node, ignoring restart"\
                                            .format(agent_id, agent_launch_file)
                                # self.cur.execute("UPDATE "+db_table_device_info+" SET device_status=%s where "
                                #                                                           "id=%s", ("ON", deviceID))
                                # self.con.commit()
                            else:
                                print '{} >> Device with MAC address {} found to be Non-BEMOSS'\
                                        .format(agent_id, macaddress)

                        #case2: new device has been discovered
                    else:
                        print '{} >> new device found with macaddress {}'\
                            .format(agent_id, macaddress)
                        newdeviceflag = True
                else:
                    print "Invalid MAC address at: {}"\
                        .format(address)
                    newdeviceflag = False

                if newdeviceflag:
                    model_info_received = False
                    try:
                        modelinfo = discovery_module.getmodelvendor(
                            discovery_type, address)
                        if modelinfo != None:
                            deviceModel = modelinfo['model']
                            deviceVendor = modelinfo['vendor']
                            print 'Model information found: '
                            print {
                                'model': deviceModel,
                                'vendor': deviceVendor
                            }
                            model_info_received = True
                    except:
                        pass

                    if model_info_received:
                        try:
                            self.cur.execute(
                                "SELECT device_type from " +
                                db_table_supported_devices +
                                " where vendor_name=%s and device_model=%s",
                                (deviceVendor, deviceModel))
                            controller_type_from_model = self.cur.fetchone()[0]
                            supported = True
                        except:
                            supported = False
                        if (supported):
                            if (controller_type
                                    == 'All') | (controller_type_from_model
                                                 == controller_type):
                                self.device_num += 1
                                #deviceType = com_type + controller_type
                                deviceType = controller_type_from_model
                                self.cur.execute(
                                    "SELECT device_model_id from " +
                                    db_table_supported_devices +
                                    " where vendor_name=%s and device_model=%s",
                                    (deviceVendor, deviceModel))
                                device_type_id = self.cur.fetchone()[0]
                                self.cur.execute(
                                    "SELECT identifiable from " +
                                    db_table_supported_devices +
                                    " where vendor_name=%s and device_model=%s",
                                    (deviceVendor, deviceModel))
                                identifiable = self.cur.fetchone()[0]
                                if (ip_address != None):
                                    if ('/' in ip_address):
                                        IPparsed = urlparse(ip_address)
                                        print IPparsed
                                        deviceIP = str(IPparsed.netloc)
                                        if str(IPparsed.scheme) != '':
                                            address = str(
                                                IPparsed.scheme) + "://" + str(
                                                    IPparsed.netloc)
                                        else:
                                            address = deviceIP
                                        if ':' in deviceIP:
                                            deviceIP = deviceIP.split(':')[0]
                                    else:
                                        if ':' in ip_address:
                                            deviceIP = ip_address.split(':')[0]
                                        else:
                                            deviceIP = ip_address
                                        address = ip_address
                                else:
                                    deviceIP = ip_address
                                self.cur.execute(
                                    "SELECT api_name from " +
                                    db_table_supported_devices +
                                    " where vendor_name=%s and device_model=%s",
                                    (deviceVendor, deviceModel))
                                deviceAPI = self.cur.fetchone()[0]
                                deviceID = device_type_id + macaddress
                                # self.cur.execute("INSERT INTO "+db_table_device_info+" VALUES(%s,%s,%s,%s,%s,%s,%s,999,%s,'ON')",
                                #                  (deviceID, deviceID, deviceType+str(self.device_num), deviceType, device_type_id,
                                #                   deviceVendor, deviceModel, macaddress))
                                self.cur.execute(
                                    "INSERT INTO " + db_table_device_info +
                                    " VALUES(%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s)",
                                    (deviceID, deviceType, deviceVendor,
                                     deviceModel, device_type_id, macaddress,
                                     None, None, identifiable, com_type,
                                     str(datetime.datetime.now()), macaddress,
                                     True))
                                self.con.commit()
                                self.cur.execute(
                                    "INSERT INTO " + deviceType + " (" +
                                    deviceType +
                                    "_id, ip_address,nickname,zone_id,network_status) VALUES(%s,%s,%s,%s,%s)",
                                    (deviceID, deviceIP, deviceType +
                                     str(self.device_num), 999, 'ONLINE'))
                                self.con.commit()
                                agent_name = deviceType.replace('_', '')
                                num_new_Devices += 1
                                #After found new device-> Assign a suitable agent to each device to communicate, control, and collect data
                                print(
                                    'Now DeviceDiscoverAgent is assigning a suitable agent to the discovered Weme device to communicate, control, and collect data'
                                )
                                self.write_launch_file(
                                    agent_name + "agent", deviceID,
                                    device_monitor_time, deviceModel,
                                    deviceVendor, deviceType, deviceAPI,
                                    address, db_host, db_port, db_database,
                                    db_user, db_password)
                                self.launch_agent(Agents_Launch_DIR,
                                                  deviceID + ".launch.json")
                                self.new_discovery = True
                            else:
                                pass
                        else:
                            print "Device currently not supported by BEMOSS"

                    else:
                        print 'Unable to get device model information. Ignoring device...'

            #Print how many WiFi devices this DeviceDiscoverAgent found!
            print("{} >> Found {} new {} {} devices".format(
                agent_id, num_new_Devices, com_type, controller_type))
            print("{} >> There are existing {} {} {} devices\n".format(
                agent_id, num_Devices, com_type, controller_type))
            print " "
            return num_new_Devices

        def launch_agent(self, dir, launch_file):
            _launch_file = os.path.join(dir, launch_file)
            os.system("bin/volttron-ctrl stop-agent " + launch_file)
            os.system("bin/volttron-ctrl load-agent " + _launch_file)
            os.system("bin/volttron-ctrl start-agent " +
                      os.path.basename(_launch_file))
            os.system("bin/volttron-ctrl list-agent")
            print "{} >> has successfully launched {} located in {}".format(
                agent_id, launch_file, dir)

        def checkMACinDB(self, conn, macaddr):
            cur = conn.cursor()
            cur.execute(
                "SELECT device_id FROM " + db_table_device_info +
                " WHERE mac_address=%(id)s", {'id': macaddr})
            if cur.rowcount != 0:
                mac_already_in_db = True
            else:
                mac_already_in_db = False
            return mac_already_in_db

        def device_agent_still_running(self, agent_launch_filename):
            os.system("bin/volttron-ctrl list-agent > running_agents.txt")
            infile = open('running_agents.txt', 'r')
            agent_still_running = False
            reg_search_term = agent_launch_filename
            for line in infile:
                #print(line, end='') #write to a next file name outfile
                match = re.search(reg_search_term, line) and re.search(
                    'running', line)
                if match:  # The agent for this device is running
                    agent_still_running = True
                else:
                    pass
            infile.close()
            return agent_still_running

        def write_launch_file(self, executable, deviceID, device_monitor_time,
                              deviceModel, deviceVendor, deviceType, api,
                              address, db_host, db_port, db_database, db_user,
                              db_password):
            data = {
                "agent": {
                    "exec":
                    executable +
                    "-0.1-py2.7.egg --config \"%c\" --sub \"%s\" --pub \"%p\""
                },
                "agent_id": deviceID,
                "device_monitor_time": device_monitor_time,
                "model": deviceModel,
                "vendor": deviceVendor,
                "type": deviceType,
                "api": api,
                "address": address,
                "db_host": db_host,
                "db_port": db_port,
                "db_database": db_database,
                "db_user": db_user,
                "db_password": db_password,
                "building_name": "bemoss",
                "zone_id": 999
            }
            __launch_file = os.path.join(Agents_Launch_DIR + deviceID +
                                         ".launch.json")
            with open(__launch_file, 'w') as outfile:
                json.dump(data, outfile, indent=4, sort_keys=True)

    Agent.__name__ = 'DeviceDiscoveryAgent'
    return Agent(**kwargs)
Esempio n. 21
0
 def __init__(self, config_path, **kwargs):
     super(IEBSubscriber, self).__init__(**kwargs)
     self.config = utils.load_config(config_path)
Esempio n. 22
0
def scheduleragent(config_path, **kwargs):
    config = utils.load_config(config_path)

    def get_config(name):
        try:
            kwargs.pop(name)
        except KeyError:
            return config.get(name, '')

    # 1. define name of this application
    app_name = "thermostat_scheduler"

    # 2. @params agent
    agent_id = get_config('agent_id')
    clock_time = 1  # schedule is updated every second
    publish_address = 'ipc:///tmp/volttron-lite-agent-publish'
    subscribe_address = 'ipc:///tmp/volttron-lite-agent-subscribe'
    debug_agent = False

    # 3. @params DB interfaces (settings file in ~/workspace/bemoss_os/)
    # 3.1 PostgreSQL (meta-data database) connection information
    db_host = settings.DATABASES['default']['HOST']
    db_port = settings.DATABASES['default']['PORT']
    db_database = settings.DATABASES['default']['NAME']
    db_user = settings.DATABASES['default']['USER']
    db_password = settings.DATABASES['default']['PASSWORD']
    db_table_application_registered = settings.DATABASES['default'][
        'TABLE_application_registered']
    db_table_application_running = settings.DATABASES['default'][
        'TABLE_application_running']
    # 3.2 sMAP (time-series data database) connection information
    # construct topic_app_sMAP based on data obtained from the launcher
    _topic_Agent_sMAP = 'datalogger/log/' + app_name + agent_id + '/'

    # 4. set exchanged topics between this app and other entities
    # 4.1 exchanged topic app and agent
    # construct topic_app_agent based on data obtained from the launcher
    topic_app_agent = '/ui/agent/' + 'building1/999/thermostat/' + agent_id + '/' + 'update'
    topic_agent_app = '/agent/ui/' + 'building1/999/thermostat/' + agent_id + '/' + 'update/response'
    # 4.2 exchanged topic ui and app
    # construct topic_ui_app based on data obtained from the launcher
    topic_ui_app = '/ui/app/' + app_name + '/' + agent_id + '/' + 'update'
    topic_app_ui = '/app/ui/' + app_name + '/' + agent_id + '/' + 'update/response'

    class Agent(PublishMixin, BaseAgent):

        # 1. agent initialization
        def __init__(self, **kwargs):
            super(Agent, self).__init__(**kwargs)
            # 1. initialize all agent variables
            self.variables = kwargs
            self.timeModeTemp = kwargs
            self.timeModeTemp.clear()
            self.flag_time_to_change_status = False
            self.current_use_schedule = None
            self.schedule_first_run = False
            self.old_day = self.find_day()
            self.active_scheduler_mode = list()
            self.time_next_schedule_sec = int(24 * 3600)
            self.weekday_list = [
                'monday', 'tuesday', 'wednesday', 'thursday', 'friday'
            ]
            self.weekend_list = ['saturday', 'sunday']
            self.current_schedule_object = None
            # 2. connect to the database
            try:
                self.con = psycopg2.connect(host=db_host,
                                            port=db_port,
                                            database=db_database,
                                            user=db_user,
                                            password=db_password)
                self.cur = self.con.cursor(
                )  # open a cursor to perfomm database operations
                print(
                    "{} for Agent: {} >> connects to the database name {} successfully"
                    .format(app_name, agent_id, db_database))
            except:
                print(
                    "ERROR: {} for Agent: {} >> fails to connect to the database name {}"
                    .format(app_name, agent_id, db_database))

            try:
                app_agent_id = app_name + '_' + agent_id
                self.cur.execute(
                    "SELECT app_setting FROM application_running WHERE app_agent_id=%s",
                    (app_agent_id, ))
                if self.cur.rowcount != 0:
                    _launch_file = str(self.cur.fetchone()[0])
                    with open(_launch_file) as json_data:
                        _new_schedule_object = json.load(json_data)
                    # pprint(_new_schedule_object)
                    # print '_launch_file' + _launch_file
                    # set self.current_schedule_object to be the new schedule
                    self.current_schedule_object = _new_schedule_object[
                        'thermostat']
                    # 3. get currently active schedule
                    self.active_scheduler_mode = list()
                    print '{} for Agent: {} >> new active schedule are as follows:'.format(
                        app_name, agent_id)
                    for each1 in self.current_schedule_object[agent_id]:
                        if each1 == 'active':
                            for each2 in self.current_schedule_object[
                                    agent_id][each1]:
                                self.active_scheduler_mode.append(each2)

                    for index in range(len(self.active_scheduler_mode)):
                        print "- " + self.active_scheduler_mode[index]

                    # 4. RESTART Scheduler Agent **************** IMPORTANT
                    self.set_query_mode_all_day()
                    self.schedule_first_run = True
                    #******************************************* IMPORTANT
                    print(
                        "{} for Agent: {} >> DONE getting data from applications_running"
                        .format(app_name, agent_id))
                else:
                    print("{} for Agent: {} >> has no previous setting before".
                          format(app_name, agent_id))
            except:
                print(
                    "{} for Agent: {} >> error getting data from applications_running"
                    .format(app_name, agent_id))

        # 2. agent setup method
        def setup(self):
            super(Agent, self).setup()
            print "{} for Agent: {} >> has been launched successfully".format(
                app_name, agent_id)
            print "{} for Agent: {} >> is waiting to be configured by the setting sent from the UI"\
                .format(app_name, agent_id)

        # 3. clockBehavior (CyclicBehavior)
        @periodic(clock_time)
        def clockBehavior(self):
            #1. check current time
            self.htime = datetime.datetime.now().hour
            self.mtime = datetime.datetime.now().minute
            self.stime = datetime.datetime.now().second
            self.now_decimal = int(self.htime) * 60 + int(self.mtime)
            self.time_now_sec = int(
                self.htime * 3600) + int(self.mtime) * 60 + int(self.stime)

            # 2. update self.schedule_first_run to True if day has change
            self.new_day = self.find_day()
            if self.new_day != self.old_day:
                if self.current_schedule_object != None:
                    #RESTART Scheduler Agent ******************* IMPORTANT
                    self.set_query_mode_all_day()
                    self.schedule_first_run = True
                    self.old_day = self.new_day
                    #******************************************* IMPORTANT
                    print '{} for Agent: {} >> today: {} is  a new day (yesterday: {}), load new schedule for today'\
                        .format(app_name, agent_id, self.new_day, self.old_day)
                else:
                    print '{} for Agent: {} >> today: {} is  a new day (yesterday: {}), but no schedule has been set yet'\
                        .format(app_name, agent_id, self.new_day, self.old_day)
            else:  # same day no reset is required
                pass

            # 3. self.schedule_first_run to True is triggered by 1. setting from UI or 2. Day change
            if self.schedule_first_run is True:
                self.schedule_action()
                self.schedule_first_run = False
            else:
                pass

            # 4. if next schedule comes up, run self.schedule_action()
            if self.time_now_sec >= self.time_next_schedule_sec:
                if debug_agent:                    print "{} for Agent: >> {} now _time_now_sec= {}, self.time_next_schedule_sec ={}"\
        .format(app_name, agent_id, self.time_now_sec, self.time_next_schedule_sec)
                print "{} for Agent: {} >> is now woken up".format(
                    app_name, agent_id)
                self.schedule_action()

        # 4. updateScheduleBehavior (GenericBehavior)
        @matching.match_exact(topic_ui_app)
        def updateScheduleBehavior(self, topic, headers, message, match):
            # print agent_id + " got\nTopic: {topic}".format(topic=topic)
            # print "Headers: {headers}".format(headers=headers)
            # print "Message: {message}\n".format(message=message)
            # take action to update new schedule to an agent
            _time_receive_update_from_ui = datetime.datetime.now()
            print "{} for Agent: {} >> got new update from UI at {}".format(
                app_name, agent_id, _time_receive_update_from_ui)
            try:
                # 1. get path to the new launch file from the message sent by UI
                _data = json.dumps(message[0])
                _data = json.loads(message[0])
                _launch_file = _data.get('path')
                print '{} for Agent: {} >> new schedule path is at: {}'.format(
                    app_name, agent_id, _launch_file)
                app_agent_id = app_name + "_" + agent_id
                print "app_agent_id = {}".format(app_agent_id)
                self.cur.execute(
                    "UPDATE application_running SET app_setting=%s WHERE app_agent_id=%s",
                    (_launch_file, app_agent_id))
                self.con.commit()
                print '{} for Agent: {} >> DONE update applications_running table with path {}'\
                    .format(app_name, agent_id, _launch_file)

                # 2. load new schedule from the new launch file
                with open(_launch_file) as json_data:
                    _new_schedule_object = json.load(json_data)
                # pprint(_new_schedule_object)
                # set self.current_schedule_object to be the new schedule
                self.current_schedule_object = _new_schedule_object[
                    'thermostat']

                # 3. get currently active schedule
                self.active_scheduler_mode = list()
                print '{} for Agent: {} >> new active schedule are as follows:'.format(
                    app_name, agent_id)
                for each1 in self.current_schedule_object[agent_id]:
                    if each1 == 'active':
                        for each2 in self.current_schedule_object[agent_id][
                                each1]:
                            self.active_scheduler_mode.append(each2)

                for index in range(len(self.active_scheduler_mode)):
                    print "- " + self.active_scheduler_mode[index]

                # 4. RESTART Scheduler Agent **************** IMPORTANT
                self.set_query_mode_all_day()
                self.schedule_first_run = True
                #******************************************* IMPORTANT

                # reply message from app to ui
                _headers = {
                    'AppName': app_name,
                    'AgentID': agent_id,
                    headers_mod.CONTENT_TYPE: headers_mod.CONTENT_TYPE.JSON,
                    headers_mod.FROM: agent_id,
                    headers_mod.TO: 'ui'
                }
                _message = 'success'
                self.publish(topic_app_ui, _headers, _message)
                print '{} for Agent: {} >> DONE update new active schedule received from UI'\
                    .format(app_name, agent_id)
            except:
                print "{} for Agent: {} >> ERROR update new schedule received from UI at {}"\
                    .format(app_name, agent_id, _time_receive_update_from_ui)
                print "{} for Agent: {} >> possible ERRORS are as follows: "\
                    .format(app_name, agent_id, _time_receive_update_from_ui)
                print "{} for Agent: {} >> ERROR 1 : path to update schedule sent by UI is incorrect "\
                    .format(app_name, agent_id, _time_receive_update_from_ui)
                print "{} for Agent: {} >> ERROR 2 : content inside schedule setting file (JSON) sent by UI" \
                      " is incorrect ".format(app_name, agent_id, _time_receive_update_from_ui)

        # Helper methods --------------------------------------
        def set_query_mode_all_day(self):
            if 'holiday' in self.active_scheduler_mode:
                # 1. find whether today is a holiday by query db 'public.holiday'
                _date_today = str(datetime.datetime.now().date())
                self.cur.execute(
                    "SELECT description FROM holiday WHERE date=%s",
                    (_date_today, ))
                if self.cur.rowcount != 0:
                    self.holiday_description = self.cur.fetchone()[0]
                    print '---------------------------------'
                    print "{} for Agent: {} >> Hoorey! today is a holiday: {}"\
                        .format(app_name, agent_id, self.holiday_description)
                    self.todayHoliday = True
                else:
                    print '---------------------------------'
                    print "{} for Agent: {} >> Today is not a holiday".format(
                        app_name, agent_id)
                    self.todayHoliday = False

                # 2. select schedule according to the day
                if self.todayHoliday is True:
                    self.today = self.find_day()
                    print "{} for Agent: {} >> mode: holiday".format(
                        app_name, agent_id)
                    self.query_schedule_mode = 'holiday'
                    self.get_schedule_time_mode_temperature()
                else:
                    self.set_query_mode_point_everyday_weekdayweekend()
                    self.get_schedule_time_mode_temperature()
            else:  # no holiday mode
                self.todayHoliday = False
                self.set_query_mode_point_everyday_weekdayweekend()
                self.get_schedule_time_mode_temperature()

        def set_query_mode_point_everyday_weekdayweekend(self):
            # find the day of today then print the setting of the Scheduler Agent
            self.today = self.find_day()
            if 'everyday' in self.active_scheduler_mode:
                print '---------------------------------'
                print "{} for Agent: {} >> mode: everyday".format(
                    app_name, agent_id)
                self.query_schedule_mode = 'everyday'
                self.query_schedule_point = self.today
            elif 'weekdayweekend' in self.active_scheduler_mode:
                print '---------------------------------'
                print '{} for Agent: {} >> mode: weekdayweekend'.format(
                    app_name, agent_id)
                self.query_schedule_mode = 'weekdayweekend'
                # find whether today is a weekday or weekend
                if self.today in self.weekday_list:
                    self.query_schedule_point = 'weekday'
                elif self.today in self.weekend_list:
                    self.query_schedule_point = 'weekend'
                else:  #TODO change this default setting
                    self.query_schedule_point = 'weekday'

        def get_schedule_time_mode_temperature(self):
            # 1. get time-related schedule including: 1.mode and 2. temperature setpoint
            self.newTimeScheduleChange = list()

            if self.todayHoliday is True:
                for eachmode in self.current_schedule_object[agent_id][
                        'schedulers'][self.query_schedule_mode]:
                    for eachtime in self.current_schedule_object[agent_id][
                            'schedulers'][self.query_schedule_mode][eachmode]:
                        #time that scheduler need to change thermostat setting
                        self.newTimeScheduleChange.append(int(eachtime['at']))
                        #dictionary relate time with mode and temperature setpoint
                        self.timeModeTemp[str(
                            eachtime['at'])] = str(eachmode + " " +
                                                   eachtime['setpoint'])
            else:
                for eachmode in self.current_schedule_object[agent_id][
                        'schedulers'][self.query_schedule_mode][
                            self.query_schedule_point]:
                    for eachtime in self.current_schedule_object[agent_id][
                            'schedulers'][self.query_schedule_mode][
                                self.query_schedule_point][eachmode]:
                        #time that scheduler need to change thermostat setting
                        self.newTimeScheduleChange.append(int(eachtime['at']))
                        #dictionary relate time with mode and temperature set point
                        self.timeModeTemp[str(
                            eachtime['at'])] = str(eachmode + " " +
                                                   eachtime['setpoint'])

            # 2. sort time to change the schedule in ascending order
            self.newTimeScheduleChange.sort()

            # 3. get the schedule: reordered time and corresponding mode and temperature setting
            if self.todayHoliday is True:
                print "{} for Agent: {} >> Today is {} and here is the schedule for holiday:"\
                    .format(app_name, agent_id, self.holiday_description)
            else:
                if self.query_schedule_mode is 'everyday':
                    print "{} for Agent: {} >> Today is {} and here is the schedule for today:"\
                        .format(app_name, agent_id, self.today)
                elif self.query_schedule_point is 'weekday':
                    print "{} for Agent: {} >> Today is {} and here is the schedule for the weekday:"\
                        .format(app_name, agent_id, self.today)
                elif self.query_schedule_point is 'weekend':
                    print "{} for Agent: {} >> Today is {} and here is the schedule for the weekend:"\
                        .format(app_name, agent_id, self.today)
            for index in range(len(self.newTimeScheduleChange)):
                # _time = float(self.newTimeScheduleChange[index])/60
                _time = self.newTimeScheduleChange[index]
                _hour = int(_time / 60)
                _minute = int(_time - (_hour * 60))
                _modetemp = self.timeModeTemp[str(
                    self.newTimeScheduleChange[index])].split()
                _mode = _modetemp[0]
                _temp = _modetemp[1]
                print "{} for Agent: {} >> At {} hr {} min mode: {}, setpoint: {} F"\
                    .format(app_name, agent_id, str(_hour), str(_minute), str(_mode).upper(), str(_temp))
            print ''

        def schedule_action(self):
            # *before call this method make sure that self.timeModeTemp and self.newTimeScheduleChange has been updated!
            print '{} for Agent: {} >> current time {} hr {} min {} sec (decimal = {})'\
                .format(app_name, agent_id, self.htime, self.mtime, self.stime, str(self.now_decimal))
            # 1. check current and past schedule
            _pastSchedule = list()  # list to save times of previous schedules
            for index in range(len(self.newTimeScheduleChange)):
                if self.now_decimal >= self.newTimeScheduleChange[index]:
                    _pastSchedule.append(self.newTimeScheduleChange[index])

            # if length of _pastSchedule is not 0, there is a previous schedule(s)
            if len(_pastSchedule) != 0:
                # previous schedule exists, use previous schedule to update thermostat to latest setting
                _latest_schedule = max(_pastSchedule)
                self.current_use_schedule = _latest_schedule
                _time_current_schedule = int(self.current_use_schedule)
                self.hour_current_schedule = int(_time_current_schedule / 60)
                self.minute_current_schedule = int(_time_current_schedule -
                                                   self.hour_current_schedule *
                                                   60)
                self.modetemp_current = self.timeModeTemp[str(
                    self.current_use_schedule)].split()
                self.modeToChange_current = self.modetemp_current[0]
                self.tempToChange_current = self.modetemp_current[1]
                self.flag_time_to_change_status = True
                print "{} for Agent: {} >> Here is the new schedule: at {} hr {} min mode = {} setpoint = {} F".\
                    format(app_name, agent_id, self.hour_current_schedule, self.minute_current_schedule,
                           str(self.modeToChange_current).upper(), self.tempToChange_current)
            else:  # previous schedule does not exist
                # scheduler is trying to get setting from the old setting file
                if self.current_use_schedule is not None:  # previous schedule from old setting file exists
                    _time_current_schedule = int(self.current_use_schedule)
                    self.hour_current_schedule = int(_time_current_schedule /
                                                     60)
                    self.minute_current_schedule = int(
                        _time_current_schedule -
                        self.hour_current_schedule * 60)
                    self.modetemp_current = self.timeModeTemp[str(
                        self.current_use_schedule)].split()
                    self.modeToChange_current = self.modetemp_current[0]
                    self.tempToChange_current = self.modetemp_current[1]
                    self.flag_time_to_change_status = True
                    print "{} for Agent: {} >> previous schedule is at {} hr {} min mode: {}, setpoint: {} F"\
                        .format(app_name, agent_id, str(self.hour_current_schedule), str(self.minute_current_schedule),
                                str(self.modeToChange_current).upper(), str(self.tempToChange_current))
                else:  # previous schedule from old setting file does not exist
                    print "{} for Agent: {} >> There is no previous schedule from old setting file".format(
                        app_name, agent_id)
                    print '{} for Agent: {} >> Let' 's check for the next schedule'.format(
                        app_name, agent_id)

            # 2. take action based on current time
            # 2.1 case 1: time to change schedule is triggered
            if self.flag_time_to_change_status is True:
                if debug_agent:                    print "{} for Agent: {} >> is changing the thermostat mode and temperature setpoint"\
        .format(app_name, agent_id)
                try:
                    _headers = {
                        headers_mod.FROM: agent_id,
                        headers_mod.CONTENT_TYPE:
                        headers_mod.CONTENT_TYPE.JSON,
                    }
                    if str(self.modeToChange_current).upper() == "HEAT":
                        _content = {
                            "thermostat_mode":
                            str(self.modeToChange_current).upper(),
                            "heat_setpoint":
                            int(self.tempToChange_current)
                        }
                    elif str(self.modeToChange_current).upper() == "COOL":
                        _content = {
                            "thermostat_mode":
                            str(self.modeToChange_current).upper(),
                            "cool_setpoint":
                            int(self.tempToChange_current)
                        }
                    else:
                        _content = {}
                    print "{} for Agent: {} >> published message to IEB with mode = {} setpoint = {} F"\
                        .format(app_name, agent_id, str(self.modeToChange_current).upper(),
                                self.tempToChange_current)
                    self.publish(topic_app_agent, _headers,
                                 json.dumps(_content))
                    self.flag_time_to_change_status = False
                except:
                    print "{} for Agent: {} >> ERROR changing status of a thermostat"\
                        .format(app_name, agent_id)
            # 2.2 case2: time to change schedule is not triggered
            else:
                pass
                # print("Scheduler Agent takes no action")

            # 3. check next schedule
            # 3.1 check whether there is a next schedule
            _nextSchedule = list()
            for index in range(len(self.newTimeScheduleChange)):
                if self.now_decimal < self.newTimeScheduleChange[index]:
                    _nextSchedule.append(self.newTimeScheduleChange[index])

            if len(_nextSchedule) != 0:
                _time_next_schedule = int(min(_nextSchedule))
                self.hour_next_schedule = int(_time_next_schedule / 60)
                self.minute_next_schedule = int(_time_next_schedule -
                                                (self.hour_next_schedule * 60))
                self.modetemp_next = self.timeModeTemp[str(
                    min(_nextSchedule))].split()
                self.modeToChange_next = self.modetemp_next[0]
                self.tempToChange_next = self.modetemp_next[1]
                print "{} for Agent: {} >> Here is the next schedule: at {} hr {} min mode = {} setpoint = {} F".\
                        format(app_name, agent_id, self.hour_next_schedule, self.minute_next_schedule,
                               str(self.modeToChange_next).upper(), self.tempToChange_next)
                self.time_next_schedule_sec = int(_time_next_schedule * 60)
                _time_to_wait = self.time_next_schedule_sec - self.time_now_sec
                if _time_to_wait >= 3600:
                    _hr_to_wait = int(_time_to_wait / 3600)
                    _min_to_wait = int(
                        (_time_to_wait - (_hr_to_wait * 3600)) / 60)
                    _sec_to_wait = int(_time_to_wait - (_hr_to_wait * 3600) -
                                       (_min_to_wait * 60))
                elif _time_to_wait > 60:
                    _hr_to_wait = 0
                    _min_to_wait = int(_time_to_wait / 60)
                    _sec_to_wait = int(_time_to_wait - (_min_to_wait * 60))
                else:
                    _hr_to_wait = 0
                    _min_to_wait = 0
                    _sec_to_wait = _time_to_wait
                print "{} for Agent: {} >> is going to sleep now until next schedule come up in {} hr {} min {} sec"\
                    .format(app_name, agent_id, _hr_to_wait, _min_to_wait, _sec_to_wait)
            else:
                print "{} for Agent: {} >> There is no next schedule".format(
                    app_name, agent_id)
                self.time_next_schedule_sec = int(24 * 3600)
            print '---------------------------------'

        def find_day(self):
            localtime = time.localtime(time.time())
            today = None
            if localtime.tm_wday == 0:
                today = 'monday'
            elif localtime.tm_wday == 1:
                today = 'tuesday'
            elif localtime.tm_wday == 2:
                today = 'wednesday'
            elif localtime.tm_wday == 3:
                today = 'thursday'
            elif localtime.tm_wday == 4:
                today = 'friday'
            elif localtime.tm_wday == 5:
                today = 'saturday'
            elif localtime.tm_wday == 6:
                today = 'sunday'
            return today

        def set_variable(self, k, v):  # k=key, v=value
            self.variables[k] = v

        def get_variable(self, k):
            return self.variables.get(k,
                                      None)  # default of get_variable is none

    Agent.__name__ = 'Thermostat Scheduler Agent'
    return Agent(**kwargs)
Esempio n. 23
0
def PlugloadAgent(config_path, **kwargs):
    config = utils.load_config(config_path)

    def get_config(name):
        try:
            value = kwargs.pop(name)
        except KeyError:
            return config.get(name, '')

    #1. @params agent
    agent_id = get_config('agent_id')
    device_monitor_time = get_config('device_monitor_time')
    LOG_DATA_PERIOD = get_config('poll_time')
    publish_address = 'ipc:///tmp/volttron-lite-agent-publish'
    subscribe_address = 'ipc:///tmp/volttron-lite-agent-subscribe'
    debug_agent = False
    #List of all keywords for a Plugload agent
    agentknowledge = dict(status=["status", "on", "off", "ON", "OFF"],
                          power=["power"],
                          energy=["energy"])
    agentAPImapping = dict(status=[], power=[], energy=[])

    #2. @params device_info
    #TODO correct the launchfile in Device Discovery Agent
    building_name = get_config('building_name')
    zone_id = get_config('zone_id')
    # room = get_config('room')
    model = get_config('model')
    device_type = get_config('type')
    address = get_config('address')
    address_get = get_config('address_get')
    address_put = get_config('address_put')
    address_post = get_config('address_post')
    vendor = get_config('vendor')
    auth_header = get_config('auth_header')
    smt_username = get_config('smt_username')
    smt_password = get_config('smt_password')
    address = get_config('address')
    _address = address
    _address = _address.replace('http://', '')
    _address = _address.replace('https://', '')
    try:  # validate whether or not address is an ip address
        socket.inet_aton(_address)
        ip_address = _address
        # print "yes ip_address is {}".format(ip_address)
    except socket.error:
        # print "yes ip_address is None"
        ip_address = None
    identifiable = get_config('identifiable')
    # mac_address = get_config('mac_address')

    #3. @params agent & DB interfaces
    #TODO delete variable topic
    topic = get_config('topic')

    #TODO get database parameters from settings.py, add db_table for specific table
    db_host = get_config('db_host')
    db_port = get_config('db_port')
    db_database = get_config('db_database')
    db_user = get_config('db_user')
    db_password = get_config('db_password')
    db_table_plugload = settings.DATABASES['default']['TABLE_plugload']
    db_table_notification_event = settings.DATABASES['default'][
        'TABLE_notification_event']

    #TODO construct _topic_Agent_UI based on data obtained from DB
    _topic_Agent_UI = building_name + '/' + str(
        zone_id) + '/' + device_type + '/' + agent_id + '/'
    # _topic_Agent_UI = 'building1/999/'+device_type+'/'+agent_id+'/'
    # print(_topic_Agent_UI)

    #TODO construct _topic_Agent_sMAP based on data obtained from DB
    # _topic_Agent_sMAP = 'datalogger/log/building1/999/'+device_type+'/'+agent_id
    _topic_Agent_sMAP = 'datalogger/log/' + building_name + '/' + str(
        zone_id) + '/' + device_type + '/' + agent_id
    # print(_topic_Agent_sMAP)

    #4. @params device_api
    api = get_config('api')
    #TODO discovery agent locate the location of api in launch file e.g. "api": "testAPI.classAPI_RadioThermostat",
    # apiLib = importlib.import_module(api)
    # print(apiLib)
    apiLib = importlib.import_module("testAPI." + api)
    print("testAPI." + api)

    #4.1 initialize plugload device object
    if vendor == 'SmartThings':
        Plugload = apiLib.API(model=model,
                              device_type=device_type,
                              api=api,
                              auth_header=auth_header,
                              smt_username=smt_username,
                              smt_password=smt_password,
                              address=address,
                              address_get=address_get,
                              address_put=address_put,
                              agent_id=agent_id)
        print(
            "{0}agent is initialized for SmartThings {1} using API={2} at {3}".
            format(agent_id, Plugload.get_variable('model'),
                   Plugload.get_variable('api'),
                   Plugload.get_variable('address')))
    else:
        Plugload = apiLib.API(model=model,
                              device_type=device_type,
                              api=api,
                              address=address,
                              agent_id=agent_id)
        print("{0}agent is initialized for {1} using API={2} at {3}".format(
            agent_id, Plugload.get_variable('model'),
            Plugload.get_variable('api'), Plugload.get_variable('address')))

    #4.2 initialize values of a Plugload
    # Plugload.set_variable('power', 0)
    # Plugload.set_variable('energy', 0)

    #5. @params notification_info
    send_notification = False
    email_fromaddr = settings.NOTIFICATION['email']['fromaddr']
    email_recipients = settings.NOTIFICATION['email']['recipients']
    email_username = settings.NOTIFICATION['email']['username']
    email_password = settings.NOTIFICATION['email']['password']
    email_mailServer = settings.NOTIFICATION['email']['mailServer']
    notify_heartbeat = settings.NOTIFICATION['heartbeat']

    class Agent(PublishMixin, BaseAgent):
        """Agent for querying WeatherUndergrounds API"""

        #1. agent initialization
        def __init__(self, **kwargs):
            #1. initialize all agent variables
            super(Agent, self).__init__(**kwargs)
            self.variables = kwargs
            self.valid_data = False
            self._keep_alive = True
            self.flag = 1
            self.topic = topic
            self.event_ids = list()
            self.time_sent_notifications = {}
            self.notify_heartbeat = notify_heartbeat
            self.ip_address = ip_address if ip_address != None else None
            #2. setup connection with db -> Connect to bemossdb database
            try:
                self.con = psycopg2.connect(host=db_host,
                                            port=db_port,
                                            database=db_database,
                                            user=db_user,
                                            password=db_password)
                self.cur = self.con.cursor(
                )  # open a cursor to perfomm database operations
                print(
                    "{} connects to the database name {} successfully".format(
                        agent_id, db_database))
            except:
                print("ERROR: {} fails to connect to the database name {}".
                      format(agent_id, db_database))
            #3. send notification to notify building admin
            self.send_notification = send_notification
            # self.send_notification_status = send_notification_status
            # if self.send_notification:
            self.subject = 'Message from ' + agent_id
            # self.text = 'Now an agent device_type {} for {} with API {} at address {} is launched!'.format(
            #     Plugload.get_variable('device_type'), Plugload.get_variable('model'),
            #     Plugload.get_variable('api'), Plugload.get_variable('address'))
            # emailService = EmailService()
            # emailService.sendEmail(email_fromaddr, email_recipients, email_username, email_password, self.subject,
            #                        self.text, email_mailServer)

        #These set and get methods allow scalability
        def set_variable(self, k, v):  #k=key, v=value
            self.variables[k] = v

        def get_variable(self, k):
            return self.variables.get(k,
                                      None)  #default of get_variable is none

        #2. agent setup method
        def setup(self):
            super(Agent, self).setup()
            #Do a one time push when we start up so we don't have to wait for the periodic
            self.timer(1, self.deviceMonitorBehavior)
            if identifiable == "True": Plugload.identifyDevice()

        #3. deviceMonitorBehavior (TickerBehavior)
        @periodic(device_monitor_time)
        def deviceMonitorBehavior(self):
            #step1: get current status of a thermostat, then map keywords and variables to agent knowledge
            try:
                Plugload.getDeviceStatus()
                #mapping variables from API to Agent's knowledge
                for APIKeyword, APIvariable in Plugload.variables.items():
                    if debug_agent:
                        print(APIKeyword, APIvariable)
                    self.set_variable(
                        self.getKeyword(APIKeyword), APIvariable
                    )  # set variables of agent from API variables
                    agentAPImapping[self.getKeyword(
                        APIKeyword
                    )] = APIKeyword  # map keyword of agent and API
            except:
                print "device connection for {} is not successful".format(
                    agent_id)

            # step3: send notification to a user if required
            if self.send_notification:
                self.track_event_send_notification()

            #step4: update PostgresQL (meta-data) database
            try:
                self.cur.execute(
                    "UPDATE " + db_table_plugload + " SET status=%s "
                    "WHERE plugload_id=%s",
                    (self.get_variable('status'), agent_id))
                self.con.commit()
                if self.get_variable('power') != None:
                    self.set_variable('power', int(self.get_variable('power')))
                    self.cur.execute(
                        "UPDATE " + db_table_plugload + " SET power=%s "
                        "WHERE plugload_id=%s",
                        (self.get_variable('power'), agent_id))
                    self.con.commit()
                #TODO calculate energy
                #TODO check ip_address
                if self.ip_address != None:
                    psycopg2.extras.register_inet()
                    _ip_address = psycopg2.extras.Inet(self.ip_address)
                    self.cur.execute(
                        "UPDATE " + db_table_plugload +
                        " SET ip_address=%s WHERE plugload_id=%s",
                        (_ip_address, agent_id))
                    self.con.commit()
                #TODO check nickname
                #TODO check zone_id
                #TODO check network_status
                #TODO check other_parameters
                #TODO last_scanned_time
                _time_stamp_last_scanned = datetime.datetime.now()
                self.cur.execute(
                    "UPDATE " + db_table_plugload +
                    " SET last_scanned_time=%s "
                    "WHERE plugload_id=%s",
                    (_time_stamp_last_scanned, agent_id))
                self.con.commit()
                # #TODO last_offline_time FIXXXXX
                # _time_stamp_last_offline = datetime.datetime.now()
                # self.cur.execute("UPDATE "+db_table_plugload+" SET last_offline_time=%s "
                #                  "WHERE plugload_id=%s",
                #                  (_time_stamp_last_offline, agent_id))
                # self.con.commit()
                print(
                    "{} updates database name {} during deviceMonitorBehavior successfully"
                    .format(agent_id, db_database))
            except:
                print("ERROR: {} fails to update the database name {}".format(
                    agent_id, db_database))

            #step5: update PostgresQL (meta-data) database
            try:
                if self.get_variable('status') is not None:
                    self.publish_logdata1()
                if self.get_variable('power') is not None:
                    self.publish_logdata2()
                if self.get_variable('energy') is not None:
                    self.publish_logdata3()
                print "success update sMAP database"
            except:
                print(
                    "ERROR: {} fails to update sMAP database".format(agent_id))

            #step6: debug agent knowledge
            if debug_agent == True:
                print("printing agent's knowledge")
                for k, v in self.variables.items():
                    print(k, v)
                print('')

            if debug_agent == True:
                print("printing agentAPImapping's fields")
                for k, v in agentAPImapping.items():
                    print(k, v)

        def getKeyword(self, APIKeyword):
            for k, v in agentknowledge.items():
                if APIKeyword in agentknowledge[k]:
                    return k
                    flag = 1
                    break
                else:
                    flag = 0
                    pass
            if flag == 0:  # if flag still 0 means that a APIKeyword is not in an agent knowledge, then add it to agent knowledge
                return APIKeyword

        #4. updateUIBehavior (generic behavior)
        @matching.match_exact('/ui/agent/' + _topic_Agent_UI + 'device_status')
        def updateUIBehavior(self, topic, headers, message, match):
            print "{} agent got\nTopic: {topic}".format(
                self.get_variable("agent_id"), topic=topic)
            print "Headers: {headers}".format(headers=headers)
            print "Message: {message}\n".format(message=message)
            #reply message
            topic = '/agent/ui/' + _topic_Agent_UI + 'device_status/response'
            # now = datetime.utcnow().isoformat(' ') + 'Z'
            headers = {
                'AgentID': agent_id,
                headers_mod.CONTENT_TYPE: headers_mod.CONTENT_TYPE.JSON,
                # headers_mod.DATE: now,
            }
            if self.get_variable('power') is not None:
                _data = {
                    'device_id': agent_id,
                    'status': self.get_variable('status'),
                    'power': self.get_variable('power')
                }
            else:
                _data = {
                    'device_id': agent_id,
                    'status': self.get_variable('status')
                }
            message = json.dumps(_data)
            message = message.encode(encoding='utf_8')
            self.publish(topic, headers, message)

        #5. deviceControlBehavior (generic behavior)
        @matching.match_exact('/ui/agent/' + _topic_Agent_UI + 'update')
        def deviceControlBehavior(self, topic, headers, message, match):
            print "{} agent got\nTopic: {topic}".format(
                self.get_variable("agent_id"), topic=topic)
            print "Headers: {headers}".format(headers=headers)
            print "Message: {message}\n".format(message=message)
            #step1: change device status according to the receive message
            if self.isPostmsgValid(message[0]):  # check if the data is valid
                setDeviceStatusResult = Plugload.setDeviceStatus(
                    json.loads(message[0]))
                # Plugload.setDeviceStatus(self.get_variable('address'), json.loads(message[0])) #convert received message from string to JSON
                #TODO need to do additional checking whether the device setting is actually success!!!!!!!!
                #step2: update agent's knowledge on this device
                Plugload.getDeviceStatus()
                # Plugload.getDeviceStatus(self.get_variable('address'))
                #step3: send reply message back to the UI
                topic = '/agent/ui/' + _topic_Agent_UI + 'update/response'
                # now = datetime.utcnow().isoformat(' ') + 'Z'
                headers = {
                    'AgentID': agent_id,
                    headers_mod.CONTENT_TYPE:
                    headers_mod.CONTENT_TYPE.PLAIN_TEXT,
                    # headers_mod.DATE: now,
                }
                if setDeviceStatusResult:
                    message = 'success'
                else:
                    message = 'failure'
            else:
                print(
                    "The POST message is invalid, check status setting and try again\n"
                )
                message = 'failure'
            self.publish(topic, headers, message)

        def isPostmsgValid(self, postmsg):  # check validity of postmsg
            dataValidity = False
            try:
                _data = json.dumps(postmsg)
                _data = json.loads(_data)
                for k, v in _data.items():
                    if k == 'status':
                        dataValidity = True
                        break
            except:
                dataValidity = True
                print("dataValidity failed to validate data comes from UI")
            return dataValidity

        #6. deviceIdentifyBehavior (generic behavior)
        @matching.match_exact('/ui/agent/' + _topic_Agent_UI + 'identify')
        def deviceIdentifyBehavior(self, topic, headers, message, match):
            print "{} agent got\nTopic: {topic}".format(
                self.get_variable("agent_id"), topic=topic)
            print "Headers: {headers}".format(headers=headers)
            print "Message: {message}\n".format(message=message)
            #step1: change device status according to the receive message
            identifyDeviceResult = Plugload.identifyDevice()
            #TODO need to do additional checking whether the device setting is actually success!!!!!!!!
            #step2: send reply message back to the UI
            topic = '/agent/ui/' + _topic_Agent_UI + 'identify/response'
            # now = datetime.utcnow().isoformat(' ') + 'Z'
            headers = {
                'AgentID': agent_id,
                headers_mod.CONTENT_TYPE: headers_mod.CONTENT_TYPE.PLAIN_TEXT,
                # headers_mod.DATE: now,
            }
            if identifyDeviceResult:
                message = 'success'
            else:
                message = 'failure'
            self.publish(topic, headers, message)

        #7. Update data to sMAP
        def publish_logdata1(self):
            headers = {
                headers_mod.FROM: agent_id,
                headers_mod.CONTENT_TYPE: headers_mod.CONTENT_TYPE.JSON,
            }
            mytime = int(time.time())
            content = {
                "status": {
                    "Readings": [[
                        mytime,
                        float(1 if self.get_variable("status") == "ON" else 0)
                    ]],
                    "Units":
                    "N/A",
                    "data_type":
                    "double"
                }
            }
            print("{} published status to an IEB".format(agent_id))
            self.publish(_topic_Agent_sMAP, headers, json.dumps(content))

        def publish_logdata2(self):
            headers = {
                headers_mod.FROM: agent_id,
                headers_mod.CONTENT_TYPE: headers_mod.CONTENT_TYPE.JSON,
            }
            mytime = int(time.time())
            content = {
                "power": {
                    "Readings": [[mytime,
                                  float(self.get_variable("power"))]],
                    "Units": "N/A",
                    "data_type": "double"
                }
            }
            print("{} published power to an IEB".format(agent_id))
            self.publish(_topic_Agent_sMAP, headers, json.dumps(content))

        def publish_logdata3(self):
            headers = {
                headers_mod.FROM: agent_id,
                headers_mod.CONTENT_TYPE: headers_mod.CONTENT_TYPE.JSON,
            }
            mytime = int(time.time())
            content = {
                "energy": {
                    "Readings": [[mytime,
                                  float(self.get_variable("energy"))]],
                    "Units": "N/A",
                    "data_type": "double"
                }
            }
            print("{} published energy to an IEB".format(agent_id))
            self.publish(_topic_Agent_sMAP, headers, json.dumps(content))

        @matching.match_exact('/ui/agent/' + _topic_Agent_UI +
                              'add_notification_event')
        def add_notification_event(self, topic, headers, message, match):
            print agent_id + " got\nTopic: {topic}".format(topic=topic)
            print "Headers: {headers}".format(headers=headers)
            print "Message: {message}".format(message=message)
            #reply message
            topic = '/agent/ui/' + _topic_Agent_UI + 'add_notification_event/response'
            # now = datetime.utcnow().isoformat(' ') + 'Z'
            headers = {
                'AgentID': agent_id,
                headers_mod.CONTENT_TYPE: headers_mod.CONTENT_TYPE.JSON,
                # headers_mod.DATE: now,
                headers_mod.FROM: agent_id,
                headers_mod.TO: 'ui'
            }
            #add event_id to self.event_ids
            _data = json.loads(message[0])
            event_id = _data['event_id']
            print "{} added notification event_id: {}".format(
                agent_id, event_id)
            self.event_ids.append(event_id)
            _data = "success"
            message = _data
            # message = json.dumps(_data)
            # message = message.encode(encoding='utf_8')
            self.publish(topic, headers, message)

        @matching.match_exact('/ui/agent/' + _topic_Agent_UI +
                              'remove_notification_event')
        def remove_notification_event(self, topic, headers, message, match):
            print agent_id + " got\nTopic: {topic}".format(topic=topic)
            print "Headers: {headers}".format(headers=headers)
            print "Message: {message}".format(message=message)
            #reply message
            topic = '/agent/ui/' + _topic_Agent_UI + 'remove_notification_event/response'
            # now = datetime.utcnow().isoformat(' ') + 'Z'
            headers = {
                'AgentID': agent_id,
                headers_mod.CONTENT_TYPE: headers_mod.CONTENT_TYPE.JSON,
                # headers_mod.DATE: now,
                headers_mod.FROM: agent_id,
                headers_mod.TO: 'ui'
            }
            #add event_id to self.event_ids
            _data = json.loads(message[0])
            event_id = _data['event_id']
            print "{} removed notification event_id: {}".format(
                agent_id, event_id)
            self.event_ids.remove(event_id)
            _data = "success"
            message = _data
            # message = json.dumps(_data)
            # message = message.encode(encoding='utf_8')
            self.publish(topic, headers, message)

        def track_event_send_notification(self):
            for event_id in self.event_ids:
                print "{} is monitoring event_id: {}\n".format(
                    agent_id, event_id)
                # collect information about event from notification_event table
                self.cur.execute(
                    "SELECT event_name, notify_device_id, triggered_parameter, comparator,"
                    "threshold, notify_channel, notify_address, notify_heartbeat  FROM "
                    + db_table_notification_event + " WHERE event_id=%s",
                    (event_id, ))
                if self.cur.rowcount != 0:
                    row = self.cur.fetchone()
                    event_name = str(row[0])
                    notify_device_id = str(row[1])
                    triggered_parameter = str(row[2])
                    comparator = str(row[3])
                    threshold = row[4]
                    notify_channel = str(row[5])
                    notify_address = row[6]
                    notify_heartbeat = row[7] if row[
                        7] is not None else self.notify_heartbeat
                    _event_has_triggered = False
                    print "{} triggered_parameter:{} self.get_variable(triggered_parameter):{} comparator:{} threshold:{}"\
                        .format(agent_id, triggered_parameter, self.get_variable(triggered_parameter), comparator, threshold)
                    #check whether message is already sent
                    try:
                        if (datetime.datetime.now() -
                                self.time_sent_notifications[event_id]
                            ).seconds > notify_heartbeat:
                            if notify_device_id == agent_id:
                                if comparator == "<":
                                    threshold = float(threshold)
                                    if self.get_variable(
                                            triggered_parameter) < threshold:
                                        _event_has_triggered = True
                                elif comparator == ">":
                                    threshold = float(threshold)
                                    if self.get_variable(
                                            triggered_parameter) > threshold:
                                        _event_has_triggered = True
                                    print "{} triggered_parameter:{} self.get_variable(triggered_parameter):{} comparator:{} threshold:{}"\
                                        .format(agent_id, triggered_parameter, self.get_variable(triggered_parameter), comparator, threshold)
                                    print "{} _event_has_triggerered {}".format(
                                        agent_id, _event_has_triggered)
                                elif comparator == "<=":
                                    threshold = float(threshold)
                                    if self.get_variable(
                                            triggered_parameter) <= threshold:
                                        _event_has_triggered = True
                                elif comparator == ">=":
                                    threshold = float(threshold)
                                    if self.get_variable(
                                            triggered_parameter) >= threshold:
                                        _event_has_triggered = True
                                elif comparator == "is":
                                    if threshold == "True":
                                        threshold = True
                                    elif threshold == "False":
                                        threshold = False
                                    else:
                                        threshold = str(threshold)
                                    if self.get_variable(
                                            triggered_parameter) is threshold:
                                        _event_has_triggered = True
                                elif comparator == "isnot":
                                    if threshold == "True":
                                        threshold = True
                                    elif threshold == "False":
                                        threshold = False
                                    else:
                                        threshold = str(threshold)
                                    if self.get_variable(triggered_parameter
                                                         ) is not threshold:
                                        _event_has_triggered = True
                                else:
                                    pass
                                if _event_has_triggered:  # notify the user if triggered
                                    #step2 notify user to notify_channel at notify_address with period notify_heartbeat
                                    if notify_channel == 'email':
                                        _email_text = '{} notification event triggered_parameter: {}, comparator: {}, ' \
                                                      'threshold: {}\n now the current status of triggered_parameter: {} is {}' \
                                            .format(agent_id, triggered_parameter, comparator, threshold,
                                                    triggered_parameter, self.get_variable(triggered_parameter))
                                        emailService = EmailService()
                                        emailService.sendEmail(
                                            email_fromaddr, notify_address,
                                            email_username, email_password,
                                            self.subject, _email_text,
                                            email_mailServer)
                                        # self.send_notification_status = True
                                        #TODO store time_send_notification for each event
                                        self.time_sent_notifications[
                                            event_id] = datetime.datetime.now(
                                            )
                                        print "time_sent_notifications is {}".format(
                                            self.
                                            time_sent_notifications[event_id])
                                        print(
                                            '{} >> sent notification message for {}'
                                            .format(agent_id, event_name))
                                        print(
                                            '{} notification event triggered_parameter: {}, comparator: {}, threshold: {}'
                                            .format(agent_id,
                                                    triggered_parameter,
                                                    comparator, threshold))
                                    else:
                                        print "{} >> notification channel: {} is not supported yet".format(
                                            agent_id, notify_channel)
                                else:
                                    print "{} >> Event is not triggered".format(
                                        agent_id)
                            else:
                                "{} >> this event_id {} is not for this device".format(
                                    agent_id, event_id)
                        else:
                            "{} >> Email is already sent, waiting for another heartbeat period".format(
                                agent_id)
                    except:
                        #step1 compare triggered_parameter with comparator to threshold
                        #step1.1 classify comparator <,>,<=,>=,is,isnot
                        #case1 comparator <
                        print "{} >> first time trigger notification".format(
                            agent_id)
                        if notify_device_id == agent_id:
                            if comparator == "<":
                                threshold = float(threshold)
                                if self.get_variable(
                                        triggered_parameter) < threshold:
                                    _event_has_triggered = True
                            elif comparator == ">":
                                threshold = float(threshold)
                                if self.get_variable(
                                        triggered_parameter) > threshold:
                                    _event_has_triggered = True
                                print "{} triggered_parameter:{} self.get_variable(triggered_parameter):{} comparator:{} threshold:{}"\
                                        .format(agent_id, triggered_parameter, self.get_variable(triggered_parameter), comparator, threshold)
                                print "{} _event_has_triggerered {}".format(
                                    agent_id, _event_has_triggered)
                            elif comparator == "<=":
                                threshold = float(threshold)
                                if self.get_variable(
                                        triggered_parameter) <= threshold:
                                    _event_has_triggered = True
                            elif comparator == ">=":
                                threshold = float(threshold)
                                if self.get_variable(
                                        triggered_parameter) >= threshold:
                                    _event_has_triggered = True
                            elif comparator == "is":
                                if threshold == "True":
                                    threshold = True
                                elif threshold == "False":
                                    threshold = False
                                else:
                                    threshold = str(threshold)
                                if self.get_variable(
                                        triggered_parameter) is threshold:
                                    _event_has_triggered = True
                            elif comparator == "isnot":
                                if threshold == "True":
                                    threshold = True
                                elif threshold == "False":
                                    threshold = False
                                else:
                                    threshold = str(threshold)
                                if self.get_variable(
                                        triggered_parameter) is not threshold:
                                    _event_has_triggered = True
                            else:
                                pass
                            print "{} >> _event_has_triggered {}".format(
                                agent_id, _event_has_triggered)
                            if _event_has_triggered:  # notify the user if triggered
                                #step2 notify user to notify_channel at notify_address with period notify_heartbeat
                                if notify_channel == 'email':
                                    _email_text = '{} notification event triggered_parameter: {}, comparator: {}, ' \
                                                  'threshold: {}\n now the current status of triggered_parameter: {} is {}' \
                                        .format(agent_id, triggered_parameter, comparator, threshold,
                                                triggered_parameter, self.get_variable(triggered_parameter))
                                    emailService = EmailService()
                                    emailService.sendEmail(
                                        email_fromaddr, notify_address,
                                        email_username, email_password,
                                        self.subject, _email_text,
                                        email_mailServer)
                                    # self.send_notification_status = True
                                    #store time_send_notification for each event
                                    self.time_sent_notifications[
                                        event_id] = datetime.datetime.now()
                                    print "{} >> time_sent_notifications is {}".format(
                                        agent_id,
                                        self.time_sent_notifications[event_id])
                                    print(
                                        '{} >> sent notification message for {}'
                                        .format(agent_id, event_name))
                                    print(
                                        '{} notification event triggered_parameter: {}, comparator: {}, threshold: {}'
                                        .format(agent_id, triggered_parameter,
                                                comparator, threshold))
                                else:
                                    print "{} >> notification channel: {} is not supported yet".format(
                                        agent_id, notify_channel)
                            else:
                                print "{} >> Event is not triggered".format(
                                    agent_id)
                        else:
                            "{} >> this event_id {} is not for this device".format(
                                agent_id, event_id)
                else:
                    pass

    Agent.__name__ = 'PlugloadAgent'
    return Agent(**kwargs)
Esempio n. 24
0
def ActuatorAgent(config_path, **kwargs):
    config = utils.load_config(config_path)
    url = config['url']
    schedule_publish_interval = int(config.get('schedule_publish_interval',
                                               60))
    heartbeat_interval = int(config.get('heartbeat_interval', 60))
    points = config.get('points', {})
    schedule_state_file = config.get('schedule_state_file')
    preempt_grace_time = config.get('preempt_grace_time', 60)
    minimum_slot_time = config.get('preempt_grace_time',
                                   preempt_grace_time * 2)

    class Agent(PublishMixin, BaseAgent):
        '''Agent to listen for requests to talk to the sMAP driver.'''
        def __init__(self, **kwargs):
            super(Agent, self).__init__(**kwargs)

            self._update_event = None
            self._update_event_time = None
            self._device_states = {}

            self.setup_schedule()

            self.setup_heartbeats()

        def setup_heartbeats(self):
            for point in points:
                heartbeat_point = points[point].get("heartbeat_point")
                if heartbeat_point is None:
                    continue

                heartbeat_handler = self.heartbeat_factory(
                    point, heartbeat_point)
                self.periodic_timer(heartbeat_interval, heartbeat_handler)

        def heartbeat_factory(self, point, actuator):
            #Stupid lack on nonlocal in 2.x
            value = [False]
            request_url = '/'.join([url, point, ACTUATOR_COLLECTION, actuator])

            publish_topic = '/'.join([point, actuator])

            def update_heartbeat_value():
                value[0] = not value[0]
                payload = {'state': str(int(value[0]))}
                try:
                    r = requests.put(request_url, params=payload)
                    self.process_smap_request_result(r, publish_topic, None)
                except requests.exceptions.ConnectionError:
                    print "Warning: smap driver not running."

            return update_heartbeat_value

        def setup_schedule(self):
            now = datetime.datetime.now()
            self._schedule_manager = ScheduleManager(
                preempt_grace_time,
                now=now,
                state_file_name=schedule_state_file)

            self.update_device_state_and_schedule(now)

        def update_device_state_and_schedule(self, now):
            self._device_states = self._schedule_manager.get_schedule_state(
                now)
            schedule_next_event_time = self._schedule_manager.get_next_event_time(
                now)
            new_update_event_time = self._get_ajusted_next_event_time(
                now, schedule_next_event_time)

            for device, state in self._device_states.iteritems():
                header = self.get_headers(state.agent_id,
                                          time=str(now),
                                          task_id=state.task_id)
                header['window'] = state.time_remaining
                topic = topics.ACTUATOR_SCHEDULE_ANNOUNCE_RAW.replace(
                    '{device}', device)
                self.publish_json(topic, header, {})

            if self._update_event is not None:
                #This won't hurt anything if we are canceling ourselves.
                self._update_event.cancel()
            self._update_event_time = new_update_event_time
            self._update_event = EventWithTime(self._update_schedule_state)
            self.schedule(self._update_event_time, self._update_event)

        def _get_ajusted_next_event_time(self, now, next_event_time):
            latest_next = now + datetime.timedelta(
                seconds=schedule_publish_interval)
            #Round to the next second to fix timer goofyness in agent timers.
            if latest_next.microsecond:
                latest_next = latest_next.replace(
                    microsecond=0) + datetime.timedelta(seconds=1)
            if next_event_time is None or latest_next < next_event_time:
                return latest_next
            return next_event_time

        def _update_schedule_state(self, unix_time):
            #Find the current slot and update the state
            now = datetime.datetime.fromtimestamp(unix_time)

            self.update_device_state_and_schedule(now)

        @matching.match_regex(topics.ACTUATOR_GET() + '/(.+)')
        def handle_get(self, topic, headers, message, match):
            point = match.group(1)
            collection_tokens, point_name = point.rsplit('/', 1)
            requester = headers.get('requesterID')
            if self.check_lock(collection_tokens, requester):
                request_url = '/'.join(
                    [url, collection_tokens, ACTUATOR_COLLECTION, point_name])
                try:
                    r = requests.get(request_url)
                    self.process_smap_request_result(r, point, requester)
                except (requests.exceptions.ConnectionError) as ex:
                    error = {'type': ex.__class__.__name__, 'value': str(ex)}
                    self.push_result_topic_pair(ERROR_RESPONSE_PREFIX, point,
                                                headers, error)
            else:
                error = {
                    'type': 'LockError',
                    'value': 'does not have this lock'
                }
                self.push_result_topic_pair(ERROR_RESPONSE_PREFIX, point,
                                            headers, error)

        @matching.match_regex(topics.ACTUATOR_SET() + '/(.+)')
        def handle_set(self, topic, headers, message, match):
            point = match.group(1)
            collection_tokens, point_name = point.rsplit('/', 1)
            requester = headers.get('requesterID')
            headers = self.get_headers(requester)
            if not message:
                error = {'type': 'ValueError', 'value': 'missing argument'}
                self.push_result_topic_pair(ERROR_RESPONSE_PREFIX, point,
                                            headers, error)
                return
            else:
                try:
                    message = jsonapi.loads(message[0])
                    if isinstance(message, bool):
                        message = int(message)
                except ValueError as ex:
                    # Could be ValueError of JSONDecodeError depending
                    # on if simplesjson was used.  JSONDecodeError
                    # inherits from ValueError
                    error = {'type': 'ValueError', 'value': str(ex)}
                    self.push_result_topic_pair(ERROR_RESPONSE_PREFIX, point,
                                                headers, error)
                    return

            if self.check_lock(collection_tokens, requester):
                request_url = '/'.join(
                    [url, collection_tokens, ACTUATOR_COLLECTION, point_name])
                payload = {'state': str(message)}
                try:
                    r = requests.put(request_url, params=payload)
                    self.process_smap_request_result(r, point, requester)
                except (requests.exceptions.ConnectionError) as ex:
                    error = {'type': ex.__class__.__name__, 'value': str(ex)}
                    self.push_result_topic_pair(ERROR_RESPONSE_PREFIX, point,
                                                headers, error)
            else:
                error = {
                    'type': 'LockError',
                    'value': 'does not have this lock'
                }
                self.push_result_topic_pair(ERROR_RESPONSE_PREFIX, point,
                                            headers, error)

        def check_lock(self, device, requester):
            device = device.strip('/')
            if device in self._device_states:
                device_state = self._device_states[device]
                return device_state.agent_id == requester
            return False

        @matching.match_exact(topics.ACTUATOR_SCHEDULE_REQUEST())
        def handle_schedule_request(self, topic, headers, message, match):
            request_type = headers.get('type')
            now = datetime.datetime.now()

            if request_type == SCHEDULE_ACTION_NEW:
                self.handle_new(headers, message, now)

            elif request_type == SCHEDULE_ACTION_CANCEL:
                self.handle_cancel(headers, now)

            else:
                self.publish_json(
                    topics.ACTUATOR_SCHEDULE_RESULT(), headers, {
                        'result': SCHEDULE_RESPONSE_FAILURE,
                        'data': {},
                        'info': 'INVALID_REQUEST_TYPE'
                    })

        def handle_new(self, headers, message, now):
            requester = headers.get('requesterID')
            taskID = headers.get('taskID')
            priority = headers.get('priority')

            try:
                requests = jsonapi.loads(message[0])
            except (ValueError, IndexError) as ex:
                # Could be ValueError of JSONDecodeError depending
                # on if simplesjson was used.  JSONDecodeError
                # inherits from ValueError

                #We let the schedule manager tell us this is a bad request.
                requests = []

            result = self._schedule_manager.request_slots(
                requester, taskID, requests, priority, now)
            success = SCHEDULE_RESPONSE_SUCCESS if result.success else SCHEDULE_RESPONSE_FAILURE

            #If we are successful we do something else with the real result data
            data = result.data if not result.success else {}

            topic = topics.ACTUATOR_SCHEDULE_RESULT()
            headers = self.get_headers(requester, task_id=taskID)
            headers['type'] = SCHEDULE_ACTION_NEW
            self.publish_json(topic, headers, {
                'result': success,
                'data': data,
                'info': result.info_string
            })

            #Dealing with success and other first world problems.
            if result.success:
                self.update_device_state_and_schedule(now)
                for preempted_task in result.data:
                    topic = topics.ACTUATOR_SCHEDULE_RESULT()
                    headers = self.get_headers(preempted_task[0],
                                               task_id=preempted_task[1])
                    headers['type'] = SCHEDULE_ACTION_CANCEL
                    self.publish_json(
                        topic, headers, {
                            'result': SCHEDULE_CANCEL_PREEMPTED,
                            'info': '',
                            'data': {
                                'agentID': requester,
                                'taskID': taskID
                            }
                        })

        def handle_cancel(self, headers, now):
            requester = headers.get('requesterID')
            taskID = headers.get('taskID')

            result = self._schedule_manager.cancel_task(requester, taskID, now)
            success = SCHEDULE_RESPONSE_SUCCESS if result.success else SCHEDULE_RESPONSE_FAILURE

            topic = topics.ACTUATOR_SCHEDULE_RESULT()
            self.publish_json(topic, headers, {
                'result': success,
                'info': result.info_string,
                'data': {}
            })

            if result.success:
                self.update_device_state_and_schedule(now)

        def get_headers(self, requester, time=None, task_id=None):
            headers = {}
            if time is not None:
                headers['time'] = time
            else:
                headers = {'time': str(datetime.datetime.utcnow())}
            if requester is not None:
                headers['requesterID'] = requester
            if task_id is not None:
                headers['taskID'] = task_id
            return headers

        def process_smap_request_result(self, request, point, requester):
            headers = self.get_headers(requester)
            try:
                request.raise_for_status()
                results = request.json()
                readings = results['Readings']
                reading = readings[0][1]
                self.push_result_topic_pair(VALUE_RESPONSE_PREFIX, point,
                                            headers, reading)
            except requests.exceptions.HTTPError as ex:
                error = {
                    'type': ex.__class__.__name__,
                    'value': str(request.text)
                }
                self.push_result_topic_pair(ERROR_RESPONSE_PREFIX, point,
                                            headers, error)
            except (ValueError, IndexError, KeyError,
                    requests.exceptions.ConnectionError) as ex:
                error = {'type': ex.__class__.__name__, 'value': str(ex)}
                self.push_result_topic_pair(ERROR_RESPONSE_PREFIX, point,
                                            headers, error)

        def push_result_topic_pair(self, prefix, point, headers, *args):
            topic = normtopic('/'.join([prefix, point]))
            self.publish_json(topic, headers, *args)

    Agent.__name__ = 'ActuatorAgent'
    return Agent(**kwargs)
Esempio n. 25
0
def OpenADRAgent(config_path, **kwargs):
    config = utils.load_config(config_path)

    def get_config(name):
        try:
            value = kwargs.pop(name)
        except KeyError:
            return config[name]

    agent_id = get_config('agentid')
    ven_id = get_config('ven_id')
    vtn_uri = get_config('vtn_uri')
    poll_interval = get_config('poll_interval')
    vtn_ids = get_config('vtn_ids')
    control_interval = get_config('control_interval')

    smap_uri = get_config('smap_uri')
    smap_source_name = get_config('smap_source_name')
    smap_series_uuid = get_config('smap_series_uuid')
    smap_source_location = get_config('smap_source_location')

    event_db = database.DBHandler()

    class Agent(PublishMixin, BaseAgent):
        '''
        This agent acts as an OpenADR VEN and publishes oadrDistributeEvent
        messages to the bus
        '''
        def __init__(self, **kwargs):
            super(Agent, self).__init__(**kwargs)
            self.events = dict()
            self.oadr_client = poll.OpenADR2(vtn_base_uri=vtn_uri,
                                             vtn_poll_interval=poll_interval,
                                             event_config={
                                                 "ven_id": ven_id,
                                                 "vtn_ids": vtn_ids,
                                                 "event_callback":
                                                 self.oadr_event
                                             },
                                             control_opts={
                                                 "signal_changed_callback":
                                                 self.signal_changed_callback,
                                                 "control_loop_interval":
                                                 control_interval
                                             })

        def signal_changed_callback(self, old_level, new_level):
            '''
            This is called whenever the `oadr2.control.EventController` 
            detects a change in "current signal level" based on active
            OpenADR2 events.
            '''
            log.debug("stubbed signal change callback")

        def oadr_event(self, updated={}, canceled={}):
            '''Callback following VTN poll
            updated and cancelled events are passed in when present'''
            log.debug("Updated & new event(s): %s", updated)
            log.debug("Canceled event(s): %s", canceled)

            headers = {
                headers_mod.CONTENT_TYPE: headers_mod.CONTENT_TYPE.JSON,
                'requesterID': agent_id
            }

            for event_id, payload in updated.iteritems():
                event_data = self.build_event_info(payload)
                self.publish_json(topics.OPENADR_EVENT, headers, event_data)

            for event_id, payload in canceled.iteritems():
                event_data = self.build_event_info(payload)
                event_data['status'] = "cancelled"
                self.publish_json(topics.OPENADR_EVENT, headers, event_data)

            current_event_state = self.get_event_state()
            self.post_to_smap(current_event_state)
            self.publish_json(topics.OPENADR_STATUS, headers,
                              {"active": (current_event_state == 'active')})

        # ----- temporary ----- #
        # a get_event_state method should probably be added to oadr2
        def get_event_state(self):
            active_event = None
            active_events = event_db.get_active_events()

            for event_id in active_events.iterkeys():
                event_data = self.get_event(event_id)

                e_start = event.get_active_period_start(event_data).replace(
                    tzinfo=pytz.utc)
                now = dt.datetime.now(pytz.utc)

                if e_start < now:
                    active_event = event_data

            return ("active" if active_event is not None else "inactive")

        # --------------------- #

        # ----- temporary ----- #
        # copied from oadr.event to provide support for get_event_state above
        def get_event(self, e_id):
            evt = event_db.get_event(e_id)

            if evt is not None:
                evt = etree.XML(evt)

            return evt

        # --------------------- #

        def build_smap_event_status_object(self, status):
            """Generate payload for POSTing current event status
            to sMAP"""

            status = (1 if status == 'active' else 0)
            current_time = int(time.time()) * 1000

            return {
                smap_source_location: {
                    "Metadata": {
                        "SourceName": smap_source_name
                    },
                    "Properties": {
                        "UnitofMeasure": "Active/Inactive"
                    },
                    "Readings": [[current_time, status]],
                    "uuid": smap_series_uuid
                }
            }

        def post_to_smap(self, status):
            """POST updated event status info to sMAP series"""

            smap_payload = self.build_smap_event_status_object(status)

            log.debug("Updating sMAP event status: %s", status)
            r = requests.post(smap_uri, data=json.dumps(smap_payload))

            if r.status_code != requests.codes.ok:
                logging.warn("sMAP update unsuccessful: %s", r.status_code)
                logging.debug("sMAP update unsuccessful: %s", r.text)

        def build_event_info(self, event_xml):
            e_id = event.get_event_id(event_xml)
            e_mod_num = event.get_mod_number(event_xml)
            e_status = event.get_status(event_xml)

            # TODO get target & resource info
            event_start_dttm = event.get_active_period_start(event_xml)
            event_start_dttm = schedule.dttm_to_str(event_start_dttm)
            signals = event.get_signals(event_xml)

            # get start/end time
            start_at = event_xml.findtext(
                'ei:eiActivePeriod/xcal:properties/xcal:dtstart/xcal:date-time',
                namespaces=event.NS_A)
            start_at = schedule.str_to_datetime(start_at)
            duration = event_xml.findtext(
                'ei:eiActivePeriod/xcal:properties/xcal:duration/xcal:duration',
                namespaces=event.NS_A)
            start_at, end_at = schedule.durations_to_dates(
                start_at, [duration])

            event_info = {
                "id": e_id,
                "mod_num": e_mod_num,
                "status": e_status,
                "start_at": start_at.strftime("%Y-%m-%d %H:%M:%S"),
                "end_at": end_at.strftime("%Y-%m-%d %H:%M:%S"),
            }
            if signals: event_info["signals"] = signals

            return event_info

    Agent.__name__ = 'OpenADRAgent'
    return Agent(**kwargs)
Esempio n. 26
0
def DemandResponseAgent(config_path, **kwargs):
    config = utils.load_config(config_path)
    
    def get_config(name):
        try:
            csp = kwargs.pop(name)
        except KeyError:
            return config[name]

    agent_id = get_config('agentid')
    rtu_path = {
        'campus': get_config('campus'),
        'building': get_config('building'),
        'unit': get_config('unit'),
    }
 
    class Agent(PublishMixin, BaseAgent):
        def setup(self):
            super(Agent, self).setup()
            headers = {
                    'Content-Type': 'text/plain',
                    'requesterID': agent_id,
            }
            #DT=datetime.datetime.now().replace(hour=0,minute=0,second=0, microsecond=0)
            #signal=settings.signal
            #self.schedule(DT,sched.Event(self.check_signal,[signal]))
            self.start_timer=self.periodic_timer(10,self.get_signal)
            
        @matching.match_exact(topics.RTU_VALUE(point='MixedAirTemperature', **rtu_path))
        def on_new_data(self, topic, headers, message, match):
            data = jsonapi.loads(message[0])
            mixed_air_temperature=data
            print(mixed_air_temperature)
            
        def __init__(self, **kwargs):
            super(Agent,self).__init__(**kwargs)
            self.after_timer = None
         
        def pre_cpp_timer(self,csp_normal):
            print("Pre-cooling for CPP Event")  #pre-cool change cooling set point
            self.pre_timer = self.periodic_timer(5, self.pre_cpp_cooling,{'csp':settings.csp_norm})
            
        def pre_cpp_cooling(self,csp):
            if csp['csp']> settings.csp_pre:
                csp['csp']=csp['csp']-1
                print(csp)
            elif csp['csp']<=settings.csp_pre:
                csp['csp']=settings.csp_pre
                self.pre_timer.cancel()
                print(csp)
                
        def accelerated_pre_cooling_timer(self,pre_slope, csp):
            print("Accelerated pre-cooling for CPP Event")
            self.pre_timer = self.periodic_timer(5, self.accelerated_pre_cooling,pre_slope,{'csp':csp})
            
        def accelerated_pre_cooling(self,pre_slope,csp):
            if csp['csp']> settings.csp_pre:
                csp['csp']=csp['csp']-1*pre_slope
                print(csp)
            elif csp['csp']<=settings.csp_pre:
                csp['csp']=settings.csp_pre
                print(csp) 
                self.pre_timer.cancel()
                
        def during_cpp(self):
           print("During CPP Event")
          
       
            
        def after_cpp_timer(self,csp_normal):
            #Pull current cooling setpoint from controller CSP
            #CSP= PULL FROM CONTROLLER (GET NEW DATA)
            
            print(csp_normal)
            print("After CPP Event, returning to normal operations")
            self.after_timer = self.periodic_timer(3, self.after_cpp_cooling, csp_normal,{'csp':80})

            #set cooling setpoint down by 1 degree every 30 minutes until it reaches normal
            
                 
        def after_cpp_cooling(self,csp_normal,csp):
            print("After_CPP_COOLING")
           
            if csp['csp'] > csp_normal:
                csp['csp']=csp['csp']-1
                print(csp)
                print(datetime.datetime.now())
            elif csp['csp'] <= csp_normal:
                self.after_timer.cancel()
                csp['csp']=csp_normal
                
                print(csp)
                self.setup()
                
        def get_signal(self):
            #Pull signal from source
        
            time_now=time.mktime(datetime.datetime.now().timetuple())
            time_pre=time.mktime(datetime.datetime.now().replace(hour=settings.pre_cpp_hour,minute=0,second=0, microsecond=0).timetuple())
            time_event=time.mktime(datetime.datetime.now().replace(hour=settings.during_cpp_hour,minute=51,second=0, microsecond=0).timetuple())
            time_after=time.mktime(datetime.datetime.now().replace(hour=settings.after_cpp_hour,minute=54,second=0, microsecond=0).timetuple())
            print(time_now)
            print(time_event)
            #PULL NORMAL COOLING SETPOINT
            csp_normal=settings.csp_norm
            if (settings.signal and time_now<time_pre):
                print ("Scheduling") 
                pre_cpp_time=datetime.datetime.now().replace(hour=settings.pre_cpp_hour,minute=25,second=10, microsecond=0)
                self.schedule(pre_cpp_time,sched.Event(self.pre_cpp_timer, (csp_normal,)))
                during_cpp_time=datetime.datetime.now().replace(hour=settings.during_cpp_hour,minute=26,second=20, microsecond=0)
                self.schedule(during_cpp_time,sched.Event(self.during_cpp))
                after_cpp_time=datetime.datetime.now().replace(hour=settings.after_cpp_hour,minute=27,second=30, microsecond=0)
                self.schedule(after_cpp_time,sched.Event(self.after_cpp_timer, (csp_normal,)))
                self.start_timer.cancel()
            elif(settings.signal and time_now>time_pre and time_now<time_event):
                print("Scheduling")
                self.start_timer.cancel()
                pre_slope=(time_event-time_now)/(3600)
                during_cpp_time=datetime.datetime.now().replace(hour=settings.during_cpp_hour,minute=46,second=20, microsecond=0)
                self.schedule(during_cpp_time,sched.Event(self.during_cpp))
                after_cpp_time=datetime.datetime.now().replace(hour=settings.after_cpp_hour,minute=47,second=10, microsecond=0)
                self.schedule(after_cpp_time,sched.Event(self.after_cpp_timer, (csp_normal,)))
                self.accelerated_pre_cooling_timer(pre_slope,csp_normal)
            elif(settings.signal and time_now>time_event and time_now<time_after):
                print("Too late to pre-cool!")
                self.start_timer.cancel()
                after_cpp_time=datetime.datetime.now().replace(hour=settings.after_cpp_hour,minute=54,second=10, microsecond=0)
                self.schedule(after_cpp_time,sched.Event(self.after_cpp_timer, (csp_normal,)))
                self.during_cpp()
            print("CPP Event Missed")
            self.setup()
                
    Agent.__name__ = 'DemandResponseAgent'
    return Agent(**kwargs)
Esempio n. 27
0
def ControllerAgent(config_path, **kwargs):
    config = utils.load_config(config_path)

    def get_config(name):
        try:
            value = kwargs.pop(name)
        except KeyError:
            return config[name]

    agent_id = get_config('agentid')
    rtu_path = {
        'campus': get_config('campus'),
        'building': get_config('building'),
        'unit': get_config('unit'),
    }
    fan_point = get_config('fan_point')

    class Agent(PublishMixin, BaseAgent):
        '''Agent to control cool fan speed with outside air temperature.

        This agent listens for outdoor temperature readings then changes
        the cool fan speed.  It demonstrates pub/sub interaction with
        the RTU Controller.

        Requirements for running this agent (or any agent wishing to
        interact with the RTU:

          * Edit the driver.ini file to reflect the sMAP key, UUID, and
            other settings for your installation.
          * Activate the project Python from the project dir: .
            bin/activate.
          * Launch the sMAP driver by starting (from the project
            directory): twistd -n smap your_driver.ini.
          * Launch the ActuatorAgent just as you would launch any other
            agent.

        With these requirements met:

          * Subscribe to the outside air temperature topic.
          * If the new reading is higher than the old reading then
            * Request the actuator lock for the rtu
          * If it receives a lock request success it randomly sets the
            cool supply fan to a new reading.
          * If it does not get the lock, it will try again the next time
            the temperature rises.
          * If the set result is a success, it releases the lock.
        '''
        def __init__(self, **kwargs):
            super(Agent, self).__init__(**kwargs)
            self.prev_temp = 0

        def change_coolspeed(self):
            '''Setup our request'''
            headers = {
                headers_mod.CONTENT_TYPE: headers_mod.CONTENT_TYPE.PLAIN_TEXT,
                'requesterID': agent_id
            }
            self.publish(topics.ACTUATOR_LOCK_ACQUIRE(**rtu_path), headers)

        @matching.match_exact(
            topics.RTU_VALUE(point='OutsideAirTemperature', **rtu_path))
        def on_outside_temp(self, topic, headers, message, match):
            '''Respond to the outside air temperature events.'''

            print "Topic: {topic}, {headers}, Message: {message}".format(
                topic=topic, headers=headers, message=message)
            #Content type is json, load it for use
            cur_temp = jsonapi.loads(message[0])
            #If temp has risen, attempt to set cool supply fan
            if cur_temp > self.prev_temp:
                self.change_coolspeed()
            self.prev_temp = cur_temp

        @matching.match_exact(topics.ACTUATOR_LOCK_RESULT(**rtu_path))
        def on_lock_result(self, topic, headers, message, match):
            '''Respond to lock result events.'''

            print "Topic: {topic}, {headers}, Message: {message}".format(
                topic=topic, headers=headers, message=message)
            if headers['requesterID'] != agent_id:
                #If we didn't request this lock, we don't care about the result
                print "Not me, don't care."
                return

            mess = jsonapi.loads(message[0])
            #If we got a success then set it at a random value
            if mess == 'SUCCESS':
                setting = random.randint(10, 90)
                headers[headers_mod.CONTENT_TYPE] = (
                    headers_mod.CONTENT_TYPE.PLAIN_TEXT)
                headers['requesterID'] = agent_id
                self.publish(topics.ACTUATOR_SET(point=fan_point, **rtu_path),
                             headers, agent_id)
            elif mess == 'RELEASE':
                #Our lock release result was a success
                print "Let go of lock"

        @matching.match_exact(
            topics.ACTUATOR_VALUE(point=fan_point, **rtu_path))
        def on_set_result(self, topic, headers, message, match):
            '''Result received, release the lock'''
            print "Topic: {topic}, {headers}, Message: {message}".format(
                topic=topic, headers=headers, message=message)
            self.publish(topics.ACTUATOR_LOCK_RELEASE(**rtu_path), headers,
                         agent_id)

    Agent.__name__ = 'ControllerAgent'
    return Agent(**kwargs)
Esempio n. 28
0
 def __init__(self, config_path, **kwargs):
     super(ScheduleExampleAgent, self).__init__(**kwargs)
     self.config = utils.load_config(config_path)
Esempio n. 29
0
 def __init__(self, config_path, **kwargs):
     super(AgentSimulator, self).__init__(**kwargs)
     self.config = utils.load_config(config_path)