예제 #1
0
class Logs(object):
	def __init__(self):
		self.config = Config()

	def init_pubnub(func):
		def wrapper(self, *args, **kwargs):
			if not hasattr(self, 'pubnub'):
				pubnub_key = self.config.get_pubnub_keys()
				self.pubnub = Pubnub(publish_key=pubnub_key['publish_key'], subscribe_key=pubnub_key['subscribe_key'])
			return func(self, *args, **kwargs)
		return wrapper

	@init_pubnub
	def subscribe(self, uuid, callback, error):
		channel = self.get_channel(uuid)
		self.pubnub.subscribe(channels=channel, callback=callback, error=error)

	@init_pubnub
	def history(self, uuid, callback, error):
		channel = self.get_channel(uuid)
		self.pubnub.history(channel=channel, callback=callback, error=error)

	def unsubscribe(self, uuid):
		if hasattr(self, 'pubnub'):
			channel = self.get_channel(uuid)
			self.pubnub.unsubscribe(channel=channel)

	@staticmethod
	def get_channel(uuid):
		return 'device-{uuid}-logs'.format(uuid=uuid)
예제 #2
0
class manageClients(Thread):

    test = os.environ
    pubnub_publish_key = os.environ['PUBNUB_PUBLISH_KEY']
    pubnub_subscribe_key = os.environ['PUBNUB_SUBSCRIBE_KEY']

    mongodb_connection = None
    collection = None

    def __init__(self):
        Thread.__init__(self)
        mongodb_connection = ConnectionToDatabase()
        manageClients.deliveries_collection = mongodb_connection.getCollection(
            "temp_deliveries")
        self.pubnub_settings = Pubnub(
            publish_key=manageClients.pubnub_publish_key,
            subscribe_key=manageClients.pubnub_subscribe_key)
        # Rename to location channel
        self.pubnub_channel = "clients_channel"
        self.genericDAO = GenericDAO()
        self.manageSteeds = manageSteeds()

    def subscriber_callback(self, message, channel):
        print(message)

        # Inserer la demande de livraison faite par le client
        if (message["msg_code"] == "XX"):
            # inserer la demande de livraison dans une collection temporaire
            id_delivery = self.genericDAO.insertObject(
                manageClients.deliveries_collection, message)

    def subscriber_error(self, message):
        print("ERROR : " + message)

    def connect(self, message):
        print("CONNECTED TO LOCATION CHANNEL")

    def reconnect(self, message):
        print("RECONNECTED TO LOCATION CHANNEL")

    def disconnect(self, message):
        print("DISCONNECTED FROM LOCATION CHANNEL")

    def subscribe(self):
        # souscrire au channel
        self.pubnub_settings.subscribe(channels=self.pubnub_channel,
                                       callback=self.subscriber_callback,
                                       error=self.subscriber_error,
                                       connect=self.connect,
                                       reconnect=self.reconnect,
                                       disconnect=self.disconnect)

    def publish(self, message):
        self.pubnub_settings.publish(channel=self.pubnub_channel,
                                     message=message)

    def unsubscribe(self):

        # se desinscire du channel
        self.pubnub_settings.unsubscribe(self.pubnub_channel)
예제 #3
0
class manageGPSLocation(Thread) :

    test = os.environ
    pubnub_publish_key = os.environ['PUBNUB_PUBLISH_KEY']
    pubnub_subscribe_key = os.environ['PUBNUB_SUBSCRIBE_KEY']

    mongodb_connection = None
    collection = None

    def __init__(self):
        Thread.__init__(self)
        mongodb_connection = ConnectionToDatabase()
        manageGPSLocation.collection = mongodb_connection.getCollection("steeds")
        self.pubnub_settings = Pubnub(publish_key=manageGPSLocation.pubnub_publish_key,subscribe_key=manageGPSLocation.pubnub_subscribe_key)
        # Rename to location channel
        self.pubnub_channel = "channel_test"
        self.genericDAO = GenericDAO()

    def subscriber_callback(self, message, channel):
        print(message)
        criteriaDict = {}
        criteriaDict["_id"]=message["_id"]

        updateDict = {}
        subUpdateDict = {}

        subUpdateDict["type"]="Point"
        subUpdateDict["coordinates"] = [message["lng"],message["lat"]]

        updateDict["location"]=subUpdateDict

        self.genericDAO.updateObjects(manageGPSLocation.collection, criteriaDict, updateDict)


    def subscriber_error(self, message):
            print("ERROR : "+message)

    def connect(self, message):
        print("CONNECTED TO LOCATION CHANNEL")

    def reconnect(self, message):
        print("RECONNECTED TO LOCATION CHANNEL")

    def disconnect(self, message):
        print("DISCONNECTED FROM LOCATION CHANNEL")

    def subscribe(self):
        # souscrire au channel
        self.pubnub_settings.subscribe(channels=self.pubnub_channel, callback=self.subscriber_callback, error=self.subscriber_error
                                       ,connect=self.connect, reconnect=self.reconnect, disconnect=self.disconnect)

    def publish(self, message):
        self.pubnub_settings.publish(channel=self.pubnub_channel, message=message)

    def unsubscribe(self):

        # se desinscire du channel
        self.pubnub_settings.unsubscribe(self.pubnub_channel)
예제 #4
0
class manageClients(Thread) :

    test = os.environ
    pubnub_publish_key = os.environ['PUBNUB_PUBLISH_KEY']
    pubnub_subscribe_key = os.environ['PUBNUB_SUBSCRIBE_KEY']

    mongodb_connection = None
    collection = None

    def __init__(self):
        Thread.__init__(self)
        mongodb_connection = ConnectionToDatabase()
        manageClients.deliveries_collection = mongodb_connection.getCollection("temp_deliveries")
        self.pubnub_settings = Pubnub(publish_key=manageClients.pubnub_publish_key,subscribe_key=manageClients.pubnub_subscribe_key)
        # Rename to location channel
        self.pubnub_channel = "clients_channel"
        self.genericDAO = GenericDAO()
        self.manageSteeds = manageSteeds()

    def subscriber_callback(self, message, channel):
        print(message)

        # Inserer la demande de livraison faite par le client
        if(message["msg_code"] == "XX") :
            # inserer la demande de livraison dans une collection temporaire
            id_delivery = self.genericDAO.insertObject(manageClients.deliveries_collection, message)




    def subscriber_error(self, message):
            print("ERROR : "+message)

    def connect(self, message):
        print("CONNECTED TO LOCATION CHANNEL")

    def reconnect(self, message):
        print("RECONNECTED TO LOCATION CHANNEL")

    def disconnect(self, message):
        print("DISCONNECTED FROM LOCATION CHANNEL")

    def subscribe(self):
        # souscrire au channel
        self.pubnub_settings.subscribe(channels=self.pubnub_channel, callback=self.subscriber_callback, error=self.subscriber_error
                                       ,connect=self.connect, reconnect=self.reconnect, disconnect=self.disconnect)

    def publish(self, message):
        self.pubnub_settings.publish(channel=self.pubnub_channel, message=message)

    def unsubscribe(self):

        # se desinscire du channel
        self.pubnub_settings.unsubscribe(self.pubnub_channel)
예제 #5
0
def main():
    publish_key = os.environ['PUBNUB_PUBLISH_KEY']
    subscribe_key = os.environ['PUBNUB_SUBSCRIBE_KEY']
    pubnub = Pubnub(publish_key, subscribe_key, ssl_on=False)
    try:
        cpu = system.SystemInfo()
        while True:
            pubnub.publish(channel="kiettv.raspberry.os", message=cpu.get_cpu_percent(), callback=None, error=callback)
            time.sleep(1)
    except KeyboardInterrupt:
        pubnub.unsubscribe(channel='kiettv.raspberry.os')
        exit(0)
예제 #6
0
class MyPubnubConn():

    def __init__(self, channel):
        self.conn = Pubnub(publish_key=publish_key, subscribe_key=subscribe_key, ssl_on=True,
                           # cipherkey randomly generated for purposes of PoC, but it is too short and needs to be
                           #   securely stored.
                           # cipher_key=cipher_key
                           )
        self.channel = channel
        self.subscribed = threading.Event()

        # server doesn't need to subscribe, only publish
        #self.conn.subscribe(channels=self.channel, callback=self.incoming_message, error=self.error_cb,
        #                     connect=self.connect_cb, reconnect=self.reconnect_cb, disconnect=self.disconnect_cb)

    def incoming_message(self, message, channel):
        pass
        #print(message)

    def error_cb(self, message):
        pass
        #print("\tERROR : " + str(message))

    def connect_cb(self, message):
        self.subscribed.set()

    def reconnect_cb(self, message):
        self.subscribed.set()
        #print("\tRECONNECTED")

    def disconnect_cb(self, message):
        self.subscribed.clear()
        #print("\tDISCONNECTED")

    def send_cb(self, message):
        pass

    def discontinue(self):
        self.conn.unsubscribe(channel=self.channel)

    def publish(self, message):
        self.conn.publish(channel=self.channel, message=message, callback=self.send_cb, error=self.error_cb)

    def wait_until_ready(self):
        self.subscribed.wait()

    def print_status(self):
        print("\nCurrently connected to channel '%s'" % self.channel)
예제 #7
0
파일: Net.py 프로젝트: zebzer/ZombieProject
class Net:
    def callback(self, message):
        print(message)

    def __init__(self, channel = "Channel-ux8kwdv1u"):
        self.channel = channel
        self.pubnub = Pubnub(publish_key=PUB_KEY,
                subscribe_key=SUB_KEY)

    def subscribe(self, callback):
        self.pubnub.subscribe(channels=self.channel,
                callback=callback, error=self.callback)

    def unsubscribe(self):
        self.pubnub.unsubscribe(channel=self.channel)

    def publish(self, message):
        self.pubnub.publish(channel=self.channel, message=message,
                callback=self.callback, error=self.callback)
예제 #8
0
class Logs(object):
    """
    This class implements functions that allow processing logs from device.

    This class is implemented using pubnub python sdk.

    For more details about pubnub, please visit: https://www.pubnub.com/docs/python/pubnub-python-sdk

    """

    def __init__(self):
        self.config = Config()

    def _init_pubnub(func):
        @wraps(func)
        def wrapper(self, *args, **kwargs):
            if not hasattr(self, 'pubnub'):
                pubnub_key = self.config.get_all()['pubnub']
                self.pubnub = Pubnub(
                    publish_key=pubnub_key['publish_key'],
                    subscribe_key=pubnub_key['subscribe_key']
                )
            return func(self, *args, **kwargs)
        return wrapper

    @_init_pubnub
    def subscribe(self, uuid, callback, error):
        """
        This function allows subscribing to device logs.
        Testing

        Args:
            uuid (str): device uuid.
            callback (function): this callback is called on receiving a message from the channel.
            error (function): this callback is called on an error event.
            For more details about callbacks in pubnub subscribe, visit here: https://www.pubnub.com/docs/python/api-reference#subscribe

        Examples:
            >>> def callback(message, channel):
            ... print(message)

            >>> def error(message):
            ... print('Error:'+ str(message))

            >>> Logs.subscribe(uuid=uuid, callback=callback, error=error)

        """

        channel = self.get_channel(uuid)
        self.pubnub.subscribe(channels=channel, callback=callback, error=error)

    @_init_pubnub
    def history(self, uuid, callback, error):
        """
        This function allows fetching historical device logs.

        Args:
            uuid (str): device uuid.
            callback (function): this callback is called on receiving a message from the channel.
            error (function): this callback is called on an error event.
            For more details about callbacks in pubnub subscribe, visit here: https://www.pubnub.com/docs/python/api-reference#history

        Examples:
            >>> def callback(message):
            ... print(message)

            >>> def error(message):
            ... print('Error:'+ str(message))

            Logs.history(uuid=uuid, callback=callback, error=error)

        """

        channel = self.get_channel(uuid)
        self.pubnub.history(channel=channel, callback=callback, error=error)

    def unsubscribe(self, uuid):
        """
        This function allows unsubscribing to device logs.

        Args:
            uuid (str): device uuid.

        """

        if hasattr(self, 'pubnub'):
            channel = self.get_channel(uuid)
            self.pubnub.unsubscribe(channel=channel)

    @staticmethod
    def get_channel(uuid):
        """
        This function returns pubnub channel for a specific device.

        Args:
            uuid (str): device uuid.

        Returns:
            str: device channel.

        """

        return 'device-{uuid}-logs'.format(uuid=uuid)
예제 #9
0
    # mark every 10 minutes
    startup_time = time.time()
    while not has_internet():
        elapsed = time.time() - startup_time
        if elapsed % 600 == 0:
            print '%s mins without internet connection...' % (elapsed/60)

    # connect to cloudant client
    client.connect()

    # subscribe to client channel
    pubnub.subscribe(channels=pubnub_channel_client, callback=callback, error=callback,
                 connect=connect, reconnect=reconnect, disconnect=disconnect)

    # ensure red led is off
    GPIO.output(RED_LED, False)

    try:
        print('Booting system...')
        GPIO.add_event_detect(PIR_PIN, GPIO.RISING, callback=alert)
        
        # indicate the system is ready
        GPIO.output(GREEN_LED, True)

        while True:
            time.sleep(100)

    finally:
        print('Shutting down...')
        pubnub.unsubscribe(channel=pubnub_channel_client)
        GPIO.cleanup()
예제 #10
0
	elif message=="Turn Left":
		print "Turn Left"
		GPIO.output(dir1,1)
		GPIO.output(dir2,0)
		GPIO.output(mtr2,1)
		GPIO.output(mtr1,1)
		sleep(0.5)
		GPIO.output(dir1,0)
		GPIO.output(dir2,0)
		GPIO.output(mtr2,0)
		GPIO.output(mtr1,0)
	elif message=="Exit":
		GPIO.cleanup()
		pubnub.unsubscribe(channel="test")
		os._exit(1)

def _error(message):
	print (message)

def _reconnect(message):
	print (message)

pubnub.subscribe(channels="test", callback=_callback, error=_error, reconnect=_reconnect)

try:
	while 1:
		pass
except KeyboardInterrupt:
	GPIO.cleanup()
	pubnub.unsubscribe(channel="test")
예제 #11
0
class Logs(object):
    """
    This class implements functions that allow processing logs from device.

    This class is implemented using pubnub python sdk.

    For more details about pubnub, please visit: https://www.pubnub.com/docs/python/pubnub-python-sdk

    """
    def __init__(self):
        self.config = Config()
        self.device = Device()

    def _init_pubnub(func):
        @wraps(func)
        def wrapper(self, *args, **kwargs):
            if not hasattr(self, 'pubnub'):
                pubnub_key = self.config.get_all()['pubnub']
                self.pubnub = Pubnub(publish_key=pubnub_key['publish_key'],
                                     subscribe_key=pubnub_key['subscribe_key'])
            return func(self, *args, **kwargs)

        return wrapper

    @_init_pubnub
    def subscribe(self, uuid, callback, error):
        """
        This function allows subscribing to device logs.
        There are fields (`m`, `t`, `s`, `c`) in the output which can be unclear. They stand for:
            m - The log message itself.
            t - The log timestamp.
            s - Is this a system message?
            c - The id of the service which produced this log (or null if the device does not support multiple containers).

        Args:
            uuid (str): device uuid.
            callback (function): this callback is called on receiving a message from the channel.
            error (function): this callback is called on an error event.
            For more details about callbacks in pubnub subscribe, visit here: https://www.pubnub.com/docs/python/api-reference#subscribe

        Examples:
            # Define callback and error.
            >>> def callback(message, channel):
            ...     print(message)
            >>> def error(message):
            ...     print('Error:'+ str(message))
            >>> Logs.subscribe(uuid=uuid, callback=callback, error=error)

        """

        channel = self.get_channel(uuid)
        self.pubnub.subscribe(channels=channel, callback=callback, error=error)

    @_init_pubnub
    def history(self, uuid, callback, error):
        """
        This function allows fetching historical device logs.

        Args:
            uuid (str): device uuid.
            callback (function): this callback is called on receiving a message from the channel.
            error (function): this callback is called on an error event.
            For more details about callbacks in pubnub subscribe, visit here: https://www.pubnub.com/docs/python/api-reference#history

        Examples:
            # Define callback and error.
            >>> def callback(message):
            ...     print(message)
            >>> def error(message):
            ...     print('Error:'+ str(message))
            Logs.history(uuid=uuid, callback=callback, error=error)

        """

        channel = self.get_channel(uuid)
        self.pubnub.history(channel=channel, callback=callback, error=error)

    def unsubscribe(self, uuid):
        """
        This function allows unsubscribing to device logs.

        Args:
            uuid (str): device uuid.

        """

        if hasattr(self, 'pubnub'):
            channel = self.get_channel(uuid)
            self.pubnub.unsubscribe(channel=channel)

    def get_channel(self, uuid):
        """
        This function returns pubnub channel for a specific device.

        Args:
            uuid (str): device uuid.

        Returns:
            str: device channel.

        """

        if not hasattr(self, 'logs_channel'):
            device_info = self.device.get(uuid)
            if 'logs_channel' in device_info:
                self.logs_channel = device_info['logs_channel']
            else:
                self.logs_channel = uuid

        return 'device-{logs_channel}-logs'.format(
            logs_channel=self.logs_channel)
예제 #12
0
class manageGPSLocation(Thread):

    test = os.environ
    pubnub_publish_key = os.environ['PUBNUB_PUBLISH_KEY']
    pubnub_subscribe_key = os.environ['PUBNUB_SUBSCRIBE_KEY']

    mongodb_connection = None
    collection = None

    def __init__(self):
        Thread.__init__(self)
        mongodb_connection = ConnectionToDatabase()
        manageGPSLocation.collection = mongodb_connection.getCollection(
            "steeds")
        self.pubnub_settings = Pubnub(
            publish_key=manageGPSLocation.pubnub_publish_key,
            subscribe_key=manageGPSLocation.pubnub_subscribe_key)
        # Rename to location channel
        self.pubnub_channel = "channel_test"
        self.genericDAO = GenericDAO()

    def subscriber_callback(self, message, channel):
        print(message)
        criteriaDict = {}
        criteriaDict["_id"] = message["_id"]

        updateDict = {}
        subUpdateDict = {}

        subUpdateDict["type"] = "Point"
        subUpdateDict["coordinates"] = [message["lng"], message["lat"]]

        updateDict["location"] = subUpdateDict

        self.genericDAO.updateObjects(manageGPSLocation.collection,
                                      criteriaDict, updateDict)

    def subscriber_error(self, message):
        print("ERROR : " + message)

    def connect(self, message):
        print("CONNECTED TO LOCATION CHANNEL")

    def reconnect(self, message):
        print("RECONNECTED TO LOCATION CHANNEL")

    def disconnect(self, message):
        print("DISCONNECTED FROM LOCATION CHANNEL")

    def subscribe(self):
        # souscrire au channel
        self.pubnub_settings.subscribe(channels=self.pubnub_channel,
                                       callback=self.subscriber_callback,
                                       error=self.subscriber_error,
                                       connect=self.connect,
                                       reconnect=self.reconnect,
                                       disconnect=self.disconnect)

    def publish(self, message):
        self.pubnub_settings.publish(channel=self.pubnub_channel,
                                     message=message)

    def unsubscribe(self):

        # se desinscire du channel
        self.pubnub_settings.unsubscribe(self.pubnub_channel)
예제 #13
0
try:
    pubnub.subscribe(channels=subchannel, callback=_kill, error=_error) ## Change to callback
   
    while True:
        previous_state = current_state
        current_state = GPIO.input(sensor)
        if current_state != previous_state:
            new_state = "HIGH" if current_state else "LOW"
            if current_state:     # Motion is Detected
                lock.acquire()
                cam.start_preview() # Comment in future
                cam.preview_fullscreen = False
                cam.preview_window = (10,10, 320,240)
                print('Motion Detected')
                
                for i in range(imgCount):
                    curTime = (time.strftime("%I:%M:%S")) + ".jpg"
                    cam.capture(curTime, resize=(320,240))
                    t = threading.Thread(target=processImage, args = (curTime,))
                    t.daemon = True
                    t.start()
                    time.sleep(frameSleep)
                cam.stop_preview()
                lock.release()
                time.sleep(camSleep)

except KeyboardInterrupt:
  cam.stop_preview()
  pubnub.unsubscribe(subchannel)
  sys.exit(0)
예제 #14
0
class AppWindow(QtGui.QMainWindow, remote.Ui_MainWindow):
    resSlot = QtCore.pyqtSignal(str, str)

    def __init__(self, parent=None, **kwargs):
        super(AppWindow, self).__init__(parent)
        self.setupUi(self)
        self.I = kwargs.get('I', None)
        self.setWindowTitle(self.I.H.version_string + ' : ' + params.get('name', '').replace('\n', ' '))
        self.pubEdit.setText("pub-c-22260663-a169-4935-9c74-22925f4398af")
        self.subEdit.setText("sub-c-3431f4ba-2984-11e6-a01f-0619f8945a4f")
        self.channelLabel.setText(self.I.hexid)
        self.resetKeys()  # Connect to pubnub

        self.resSlot.connect(self.writeResults)
        self.thingSpeakCommand = None

        self.timer = QtCore.QTimer()
        self.timer.timeout.connect(self.uploadToThingSpeak)
        self.uploadToThingSpeak();
        self.timer.start(15 * 1e3)  # 15 seconds

        import inspect

        funcs = dir(self.I)
        self.methods = {}
        self.function_list = []
        for a in funcs:
            fn = getattr(self.I, a)
            try:
                args = inspect.getargspec(fn).args
            except:
                args = []

            if len(args) > 0:
                if inspect.ismethod(fn):
                    self.methods[a] = (fn, args)  # list of tuples of all methods in device handler
                    if args[0] == 'self': self.function_list.append([a, args[1:]])

    def uploadToThingSpeak(self):
        if self.thingSpeakCommand:
            try:
                result = self.thingSpeakCommand[0](*self.thingSpeakCommand[1])
                params = urllib.urlencode({'field1': float(result), 'key': str(self.thingSpeakKey.text())})
                headers = {"Content-type": "application/x-www-form-urlencoded", "Accept": "text/plain"}
                conn = httplib.HTTPConnection("api.thingspeak.com:80")
                conn.request("POST", "/update", params, headers)
                response = conn.getresponse()
                self.results_2.append('%s : %s' % (response.status, response.reason))
                data = response.read()
                conn.close()
            except Exception as e:
                self.results_2.append('Error : %s' % (e.message))
                pass

    def setThingSpeakCommand(self):
        try:
            message = str(self.cmdEditThingSpeak.text())
            fn_name = message.split('(')[0]
            args = message[message.find("(") + 1:message.find(")")].strip().split(',')
            total_args = []
            for t in args:
                if not len(t): continue
                if t[0] == "'" or t[0] == '"':
                    total_args.append(t[1:-1])
                else:
                    total_args.append(string.atoi(t))

            method = self.methods.get(fn_name)[0]
            if method == None:
                print('no such command :', fn_name)
                return 'no such command : %s' % fn_name
            else:
                # while self.hw_lock and self.active: pass
                # self.hw_lock=True
                self.thingSpeakCommand = [method, total_args]
        except Exception as e:
            self.results_2.append('Set Error : %s' % (e.message))
            pass

    def callback(self, message, channel):
        msg_type = message[0]
        message = str(message)[1:]
        try:
            if msg_type == 'Q':  # Query
                self.resSlot.emit(message, 'in')
                fn_name = message.split('(')[0]
                args = message[message.find("(") + 1:message.find(")")].strip().split(',')
                total_args = []
                for t in args:
                    if not len(t): continue
                    if t[0] == "'" or t[0] == '"':
                        total_args.append(t[1:-1])
                    else:
                        total_args.append(string.atoi(t))

                method = self.methods.get(fn_name)[0]
                if method == None:
                    print('no such command :', fn_name)
                    return 'no such command : %s' % fn_name
                else:
                    # while self.hw_lock and self.active: pass
                    # self.hw_lock=True
                    result = method(*total_args)
                    # self.hw_lock=False
                    jsonres = json.dumps(result, cls=NumpyEncoder)
                    self.pubnub.publish(channel=self.I.hexid,
                                        message='R' + jsonres)  # R stands for response . Q for Query
                    self.resSlot.emit('%s %s %s %d %s... %s' % (
                        method.__name__, str(total_args), str(type(jsonres)), len(jsonres), str(jsonres[:20]),
                        self.I.hexid + 'response'), 'out')
            elif msg_type == 'R':
                self.resSlot.emit(str(message), 'reply')

        except Exception as e:
            self.responseLabel.setText(e.message)

    def writeResults(self, txt, t):
        if t == 'in':
            self.results.append('RECV:<span style="background-color: #FFFF00">' + txt + '</span')
        elif t == 'out':
            self.results.append('SEND:<span style="background-color: #00FF00">' + txt + '</span')
        elif t == 'reply':
            self.results.append('GOT :<span style="background-color: #00FFFF">' + txt + '</span')

    def execRemote(self):
        chan = hex(0x1000000000000000 | int(str(self.sendID.text()), 16));
        msg = str(self.cmdEdit.text())
        self.pubnub.publish(channel=chan, message='Q' + msg)
        self.resSlot.emit('[' + chan + ']: ' + msg, 'out')

    def setListenState(self, state):
        if state:  # Enable listen
            try:
                self.pubnub.subscribe(self.I.hexid, callback=self.callback)
            except Exception as e:
                self.responseLabel.setText(e)
        else:
            self.pubnub.unsubscribe(self.I.hexid)

    def resetKeys(self):
        try:
            from pubnub import Pubnub
            self.pubnub = Pubnub(
                publish_key=str(self.pubEdit.text()),
                subscribe_key=str(self.subEdit.text()))
        except Exception as e:
            self.responseLabel.setText(e)

    def __del__(self):
        try:
            self.pubnub.unsubscribe(self.I.hexid)
        except:
            pass

    # try:self.pubnub.unsubscribe(self.I.hexid+'response')
    # except:pass

    def closeEvent(self, event):
        try:
            self.pubnub.unsubscribe(self.I.hexid)
        except:
            pass
        # try:self.pubnub.unsubscribe(self.I.hexid+'response')
        # except:pass

        self.finished = True
예제 #15
0
class manageSteeds(Thread) :

    test = os.environ
    pubnub_publish_key = os.environ['PUBNUB_PUBLISH_KEY']
    pubnub_subscribe_key = os.environ['PUBNUB_SUBSCRIBE_KEY']

    mongodb_connection = None
    collection = None

    def __init__(self):
        Thread.__init__(self)
        mongodb_connection = ConnectionToDatabase()

        self.steeds_collection = mongodb_connection.getCollection("available_steeds")
        self.deliveries_collection = mongodb_connection.getCollection("temp_deliveries")
        self.deliveries_steeds_collection = mongodb_connection.getCollection("temp_deliveries_steeds")

        self.pubnub_settings = Pubnub(publish_key=manageSteeds.pubnub_publish_key,subscribe_key=manageSteeds.pubnub_subscribe_key)
        # Rename to location channel
        self.pubnub_channel = "steeds_channel"

        self.genericDAO = GenericDAO()

        self.scheduler = sched.scheduler() # Instansiate a scheduler

    def subscriber_callback(self, message, channel):
        x = 1

    def subscriber_error(self, message):
            print("ERROR : "+message)

    def connect(self, message):
        print("CONNECTED TO STEEDS CHANNEL")

    def reconnect(self, message):
        print("RECONNECTED TO STEEDS CHANNEL")

    def disconnect(self, message):
        print("DISCONNECTED FROM STEEDS CHANNEL")

    def subscribe(self):
        # souscrire au channel
        self.pubnub_settings.subscribe(channels=self.pubnub_channel, callback=self.subscriber_callback, error=self.subscriber_error
                                       ,connect=self.connect, reconnect=self.reconnect, disconnect=self.disconnect)

    def publish(self, message):
        self.pubnub_settings.publish(channel=self.pubnub_channel, message=message)

    def unsubscribe(self):

        # se desinscire du channel
        self.pubnub_settings.unsubscribe(self.pubnub_channel)
# how to instansiate a scheduler


    #Out of Pubnub functions

    def manageClientDeliveryRequest(self, iddelivery):

        # Search delivery by id (criteria)
        delivery_criteria = {}
        delivery_criteria["_id"] = iddelivery

        temp_delivery = self.genericDAO.getOneObject(self, self.deliveries_colllection, delivery_criteria)

        #Extract pickup coordinates from temporary delivery
        client_coordinates = [temp_delivery["pickup_lng"], temp_delivery["pickup_lat"]]

        # Get nearest steeds to pickup location
        nearest_steeds = self.genericDAO.getNearestSteeds(self, self.steeds_collection, client_coordinates)

        # Save available steeds for delivery
        delivery_steeds = {}
        delivery_steeds["iddelivery"] = iddelivery
        delivery_steeds["available_steeds"] = nearest_steeds

        self.genericDAO.insertObject(self.deliveries_steeds_collection, delivery_steeds)

        #Send delivery request to seeder
        self.sendDeliveryRequestToSteed(iddelivery)

    def sendDeliveryRequestToSteed(self, iddelivery):

        # Search delivery by id (criteria)
        delivery_criteria = {}
        delivery_criteria["_id"] = iddelivery

        temp_delivery = self.genericDAO.getOneObject(self, self.deliveries_colllection, delivery_criteria)

        #Check received_positive_response field
        received_response = temp_delivery["received_positive_response"]

        if(not(received_response)) :

            #Search available steeds for delivery
            delivery_steeds_criteria = {}
            delivery_steeds_criteria["iddelivery"] = iddelivery

            available_steeds = self.genericDAO.getOneObject(self, self.deliveries_steeds_collection, delivery_steeds_criteria)


            #Send steed delivery request
            self.publish("XXXX"+iddelivery+available_steeds[0]["_id"]+" "+"Other delivery details")

            #Delete 1st steed from available steeds list
            new_available_steeds = available_steeds[1:len(available_steeds)-1]

            #Update delivery available steeds

            update_search_criteria = {}
            update_search_criteria["iddelivery"] = iddelivery

            update_field = {}
            update_field["available_steeds"] = new_available_steeds


            #Add update function to Generic DAO from old projects


            #Schedule method call
            self.scheduler.enter(10, 1, self.sendDeliveryRequestToSteed(iddelivery) )
            self.scheduler.run()
예제 #16
0
class Thermostat(threading.Thread, metaclass=utils.Singleton):
    """ Main class.

    Contains decision making algorithm. Gathers all available input and data from sources.
    Maintains settings and history in memory.
    Spawns a number of daemon threads to perform background tasks.

    Notes:
        - All floating point arithmetic should use Decimal type, pass in number as str to maintain precision
    """

    def __init__(self):
        super().__init__()
        self._settings = OrderedDict(sorted(utils.init_settings().items(), key=lambda t: t[0]))
        self._history = utils.init_history()
        self._current_temperature = Decimal(0)
        self._temperature_range = (Decimal(0), Decimal(0))
        self._on_rpi = utils.on_rpi()
        self._mode = utils.Mode.OFF
        self._state = utils.State.IDLE

        self.logger = getLogger('app.thermostat')
        self.temperature_offset = Decimal('0.8')  # DEMO value, original Decimal('1.5')
        self.last_state_update = 0

        self.cost_table = None  # must init after thread starts
        self.pubnub = Pubnub(
            publish_key=config.PUBLISH_KEY,
            subscribe_key=config.SUBSCRIBE_KEY,
            uuid=config.THERMOSTAT_ID,
        )
        self.weather_thread = weather.WeatherAPI(
            self._settings['temperature_unit'],
            self._settings['city'],
            self._settings['country_code'],
        )
        # TODO: actively record user actions (input range, changes after prediction)
        self.history_thread = threading.Timer(600, self.set_history)
        self.history_thread.daemon = True
        self.locks = {
            'settings': threading.Lock(),
            'temperature_range': threading.Lock(),
        }

    def run(self):
        """ Entry method.

        Main event loop is here.
        """
        # initialize db
        self.cost_table = sql.CostTable()

        # initialize sensor and pins
        if self.on_rpi:
            utils.init_rpi()
            sensor.init_sensor()
            self.current_temperature = sensor.read_temperature()

        # start daemon thread to fetch weather data
        self.weather_thread.start()

        # start daemon thread to record history data
        self.history_thread.start()

        # subscribe to remote access messages
        self.pubnub.subscribe(
            channels=config.THERMOSTAT_ID,
            callback=self._callback,
            error=self._error,
        )

        while True:
            if self.mode != utils.Mode.OFF:
                self.update_state()
            time.sleep(config.UPDATE_INTERVAL)

    def stop(self):
        """ Exit handler.

        Write data stored in memory back to files.
        """
        self.pubnub.unsubscribe(config.THERMOSTAT_ID)
        utils.write_to_file(config.SETTINGS_FILENAME, self._settings)
        utils.write_to_file(config.HISTORY_FILENAME, self._history)
        self.cost_table.close()
        self.logger.info('cleanup completed')

    def toggle_power(self):
        self.mode = utils.Mode.AUTO if self.mode == utils.Mode.OFF else utils.Mode.OFF
        self.state = utils.State.IDLE
        # self.last_state_update = time.time()
        self.publish_mode()

    def toggle_mode(self, mode=None):
        if isinstance(mode, utils.Mode):
            self.mode = mode
        else:
            if self.mode == utils.Mode.AUTO:
                self.mode = utils.Mode.HEAT
            elif self.mode == utils.Mode.HEAT:
                self.mode = utils.Mode.COOL
            elif self.mode == utils.Mode.COOL:
                self.mode = utils.Mode.AUTO
        self.publish_mode()

    def update_state(self):
        """ Decision maker.

        Gathers input and data to update thermostat state.
        """
        self.logger.debug('thermostat state is {0}'.format(self.state))
        if self.on_rpi:
            self.current_temperature = sensor.read_temperature()

        low, high = self.temperature_range
        if self.current_temperature < (low - self.temperature_offset):
            new_state = utils.State.HEAT
        elif self.current_temperature > (high + self.temperature_offset):
            new_state = utils.State.COOL
        else:
            # within range, make decision
            new_state = self.make_decision()

        # prevent oscillation
        if (time.time() - self.last_state_update) > config.OSCILLATION_DELAY:
            if self.state != new_state:
                self.last_state_update = time.time()

            # check mode to determine if new state is allowed
            if self.mode == utils.Mode.HEAT:
                self.state = new_state if new_state != utils.State.COOL else self.state
            elif self.mode == utils.Mode.COOL:
                self.state = new_state if new_state != utils.State.HEAT else self.state
            else:
                self.state = new_state
            self.publish_state()
            self.logger.debug('thermostat state updated to {0}'.format(self.state))

    def make_decision(self):
        params_list = list()

        # TODO: likely needs an additional multiplier to amplify difference
        # TODO: score is always max when this is the only parameter in DM
        rating = (self.temperature_range_equilibrium - self.current_temperature) * (self.temperature_range_ceiling - self.temperature_range_floor)
        params_list.append(('internal_temperature', rating))

        # use external temperature if the data is recent
        # TODO: factor in external humidity
        if (datetime.datetime.now() - self.weather_thread.last_updated).total_seconds() < 3600:
            rating = (self.temperature_range_ceiling - self.weather_thread.temperature) / 10
            params_list.append(('external_temperature', rating))

        # use history if the data exists
        past_temperature = self.get_history()
        if past_temperature is not None:
            rating = past_temperature - self.current_temperature
            params_list.append(('history_temperature', rating))

        # use energy cost if data exists
        cost_data = self.cost_table.select(
            select='start_time,cost',
            where={'country_code': self._settings['country_code'], 'city': self._settings['city']}
        )
        if cost_data:
            cost_data = dict(cost_data)
            lowest_cost = min(cost_data.values())
            current_hour = utils.round_time(datetime.datetime.now(), 3600).hour
            current_cost = cost_data.get(current_hour)
            if current_cost is not None:
                ratio = Decimal(lowest_cost) / Decimal(current_cost)
                rating = ratio * params_list[0][1]  # ratio * (internal temperature rating)
                params_list.append(('energy_cost', rating))

        matrix = decision.build_decision_matrix(params_list)
        return decision.evaluate_decision_matrix(matrix)

    def get_setting(self, name):
        """ Get setting value.

        :param name: setting name
        :return: setting value, None if setting does not exist
        """
        return self._settings.get(name)

    def set_setting(self, name, value):
        """ Set setting value.

        :param name: setting name
        :param value: setting value
        :return: None
        """
        if name in self._settings:
            self._settings[name] = value

    def get_history(self, dt=None):
        """ Get temperature at specified datetime.

        If dt is None, defaults to current time. Full datetime is needed to evaluate day of week.

        :param dt: datetime.datetime object
        :return: temperature
        """
        if dt is None:
            dt = datetime.datetime.now()
        elif not isinstance(dt, datetime.datetime):
            self.logger.error('must be datetime object')
            return None

        rounded_dt = utils.round_time(dt)
        day = utils.WeekDay(rounded_dt.weekday()).name
        time_block = rounded_dt.strftime('%H:%M')
        temperature = self._history[day][time_block]
        return Decimal(temperature) if temperature is not None else None

    def set_history(self, dt=None, temperature=None):
        """ Set temperature at specified datetime.

        If dt is None, defaults to current time. Full datetime is need to evaluate day of week.
        If temperature is None, defaults to current temperature.

        :param dt: datetime.datetime object
        :param temperature: temperature to record
        :return: None
        """
        if temperature is None:
            temperature = self.current_temperature

        if dt is None:
            dt = datetime.datetime.now()
        elif not isinstance(dt, datetime.datetime):
            self.logger.error('must be datetime object')
            return None

        rounded_dt = utils.round_time(dt)
        day = utils.WeekDay(rounded_dt.weekday()).name
        time_block = rounded_dt.strftime('%H:%M')
        self._history[day][time_block] = str(temperature)  # store as str to avoid conversion to JSON float

    def publish_temperatures(self, current=True, range=True):
        data = {
            'action': 'temperature_data',
            'data': {
                'current_temperature': round(self.current_temperature) if current else None,
                'temperature_low': round(self.temperature_range_floor) if range else None,
                'temperature_high': round(self.temperature_range_ceiling) if range else None,
            }
        }
        self.pubnub.publish(config.THERMOSTAT_ID, data, error=self._error)
        self.logger.debug('published message: {0}'.format(data))

    def publish_mode(self):
        data = {
            'action': 'mode_data',
            'data': {
                'mode': str(self.mode).lower()
            }
        }
        self.pubnub.publish(config.THERMOSTAT_ID, data, error=self._error)
        self.logger.debug('published message: {0}'.format(data))

    def publish_state(self):
        data = {
            'action': 'state_data',
            'data': {
                'state': str(self.state).lower()
            }
        }
        self.pubnub.publish(config.THERMOSTAT_ID, data, error=self._error)
        self.logger.debug('published message: {0}'.format(data))

    def publish_settings(self):
        data = {
            'action': 'settings_data',
            'data': utils.prettify_settings(self.settings)
        }
        self.pubnub.publish(config.THERMOSTAT_ID, data, error=self._error)
        self.logger.debug('published message: {0}'.format(data))

    def publish_history(self):
        data = {
            'action': 'history_data',
            'data': utils.get_history_graph_data_webapp(self._history)
        }
        self.pubnub.publish(config.THERMOSTAT_ID, data, error=self._error)

    def _callback(self, message, channel):
        """ Pubnub message callback.

        :param message: received payload
        :param channel: channel name
        :return: None
        """
        self.logger.debug(message)

        if message['action'] == 'request_temperatures':
            self.publish_temperatures(range=(message['value'] == 'all'))
        elif message['action'] == 'request_mode':
            self.publish_mode()
        elif message['action'] == 'request_settings':
            self.publish_settings()
        elif message['action'] == 'request_history':
            self.publish_history()
        elif message['action'] == 'update_temperature_range':
            low = message.get('temperature_low')
            high = message.get('temperature_high')
            if low is not None and high is not None:
                self.temperature_range = (Decimal(low), Decimal(high))
        elif message['action'] == 'update_mode':
            mode = message.get('mode')
            if mode is not None:
                mode = utils.Mode[mode.upper()]
                self.toggle_mode(mode)
        elif message['action'] == 'update_setting':
            name = message.get('setting_name')
            value = message.get('setting_value')
            if name is not None and value is not None:
                self.settings = utils.unprettify_setting_name(self.settings, name, value)

    def _error(self, message):
        """ Pubnub error callback.

        :param message:
        :return: None
        """
        self.logger.error(message)

    @staticmethod
    def validate_temperature(value):
        if value < config.MIN_TEMPERATURE:
            raise errors.TemperatureValidationError('Temperature cannot be below {}'.format(config.MIN_TEMPERATURE))
        if value > config.MAX_TEMPERATURE:
            raise errors.TemperatureValidationError('Temperature cannot be above {}'.format(config.MAX_TEMPERATURE))

    @property
    def on_rpi(self):
        return self._on_rpi

    @property
    def is_on(self):
        return self.mode != utils.Mode.OFF

    @property
    def is_active(self):
        return self.state != utils.State.IDLE

    @property
    def mode(self):
        return self._mode

    @mode.setter
    def mode(self, mode):
        if self.on_rpi and mode == utils.Mode.OFF:
            GPIO.output(config.FAN_PIN, config.RELAY_OFF)
            GPIO.output(config.HEAT_PIN, config.RELAY_OFF)
            GPIO.output(config.COOL_PIN, config.RELAY_OFF)
        self._mode = mode

    @property
    def state(self):
        return self._state

    @state.setter
    def state(self, state):
        if self.on_rpi:
            if state == utils.State.IDLE:
                GPIO.output(config.FAN_PIN, config.RELAY_OFF)
                GPIO.output(config.HEAT_PIN, config.RELAY_OFF)
                GPIO.output(config.COOL_PIN, config.RELAY_OFF)
            elif state == utils.State.HEAT:
                GPIO.output(config.FAN_PIN, config.RELAY_ON)
                GPIO.output(config.HEAT_PIN, config.RELAY_ON)
                GPIO.output(config.COOL_PIN, config.RELAY_OFF)
            elif state == utils.State.COOL:
                GPIO.output(config.FAN_PIN, config.RELAY_ON)
                GPIO.output(config.HEAT_PIN, config.RELAY_OFF)
                GPIO.output(config.COOL_PIN, config.RELAY_ON)
        self._state = state

    @property
    def settings(self):
        return self._settings

    @settings.setter
    def settings(self, t):
        # TODO: validation
        self.locks['settings'].acquire(False)
        key, value = t
        if self._settings.get(key):
            # TODO: make country code changeable
            if key == 'city':
                self.weather_thread.location = (value, self.weather_thread._location['country_code'])
            self._settings[key] = value
            self.publish_settings()
        self.locks['settings'].release()

    @property
    def current_temperature(self):
        return self._current_temperature

    @current_temperature.setter
    def current_temperature(self, value):
        self._current_temperature = value

    @property
    def temperature_range(self):
        return self._temperature_range

    @temperature_range.setter
    def temperature_range(self, t_range):
        self.locks['temperature_range'].acquire(False)
        try:
            low, high = t_range
            if high < low:
                raise errors.TemperatureValidationError('Invalid range.')
            self.validate_temperature(low)
            self.validate_temperature(high)
            self._temperature_range = t_range
            # send update
            self.publish_temperatures(current=False)
        finally:
            self.locks['temperature_range'].release()

    @property
    def temperature_range_floor(self):
        return min(self.temperature_range)

    @property
    def temperature_range_ceiling(self):
        return max(self.temperature_range)

    @property
    def temperature_range_equilibrium(self):
        # DEMO value
        return self.temperature_range_ceiling

        # biased towards range ceiling
        low, high = self.temperature_range
        bias = (high - low) / 4
        equilibrium = sum(self.temperature_range) / 2
        return equilibrium + bias
예제 #17
0
class BaseAppiumTest(unittest.TestCase):
    
    driver = None
    screenshot_count = 0
    screenshot_dir = None
    desired_capabilities_cloud = {}
    ignoreBuddyTests = 0
    buddyCommandName = ""
    buddyName = ""
    buddyCheckFileName = ""
    buddyCheckComplete = False
    channel = "Trilleon-Automation"
    DBID = ""
    project_id = ""
    gridIdentity = ""
    buddyIdentity = ""
    pubnub = None
    jsonGridPrefix = ""
    jsonBuddyGridPrefix = ""
    heartbeat_index = 1
    last_heartbeat_detected = None
    max_time_since_heartbeat = 80
    partialDataDelimiter = "$$$"
    
    fatalErrorDetected = False
    fatalErrorMessage = ""
    
    ready = False
    started = False
    complete = False
    timeout_default = 300
    test_execution_timeout = 4800
    parsing_xml = False
    results = ""
    auto_screenshot_index = 0
    run_command = ""
    test_run_id_internal = ""

    def setUp(self, appium_url=None, platform_name=None, bundle_id = None, application_file=None, application_package=None, screenshot_dir=None,
                 application_activity=None, automation_name=None):
        global test_run_id
        test_run_id = str(uuid.uuid4())
        self.test_run_id_internal = test_run_id
        log("test_run_id [" + test_run_id + "]")
        with open("test_run_id.txt", "w") as f:
            f.write(test_run_id)

        self.pubnub = Pubnub(publish_key="TODO: YOUR KEY HERE!",subscribe_key="TODO: YOUR KEY HERE!")
        self.setChannelPrefixes()
        self.pubnub.subscribe(channels=self.channel, callback=callback, error=error)
        self.set_screenshot_dir('%s/screenshots' % (os.getcwd()))
        
        # Buddy test run information.
        self.buddyName = "Trilleon-Automation-" + os.environ.get('BUDDY')
        self.ignoreBuddyTests = os.environ.get('IGNOREBUDDYSYSTEM')
        self.buddyCommandName = "manual_set_buddy_" + os.environ.get('BUDDY_RELATIONSHIP')
        
        # Set Buddy Check-In file path.
        directoryPieces = os.getcwd().split("/")
        parentDirectory = ""
        index = len(directoryPieces)
        for piece in directoryPieces:
            index -= 1
            if index > 0:
                parentDirectory += piece + "/"
        self.buddyCheckFileName = parentDirectory + self.buddyName.replace(".", "").replace("-", "") + ".txt"
        
        # Set test run command.
        self.run_command = os.environ.get('RUN_COMMAND')
        with open("testresultsjson.txt", "w") as f:
            f.write("[")

        # Set up driver
        if os.environ['DEVICE_PLATFORM'] == "ios":
            self.desired_capabilities_cloud["showXcodeLog"] = True
            self.desired_capabilities_cloud["useNewWDA"] = False
            self.desired_capabilities_cloud["wdaLocalPort"] = os.environ['WDA_LOCAL_PORT']
            self.desired_capabilities_cloud["autoAcceptAlerts"] = True # iOS -- Dismisses device level popups automatically.
        else:
            self.desired_capabilities_cloud["systemPort"] = os.environ['UNIQUE_BOOT_PORT']
            self.desired_capabilities_cloud["autoGrantPermissions"] = True # Android 1.6.3+ -- Gives app permissions before starting.
        self.desired_capabilities_cloud["app"] = os.environ['APPLICATION']
        self.desired_capabilities_cloud["udid"] = os.environ['DEVICE_UDID']
        self.desired_capabilities_cloud["automationName"] = os.environ['DRIVER']
        self.desired_capabilities_cloud["platformName"] = os.environ['DEVICE_PLATFORM']
        if 'PLATFORM_VERSION' in os.environ:
            self.desired_capabilities_cloud["platformVersion"] = os.environ['PLATFORM_VERSION']
        self.desired_capabilities_cloud["deviceName"] = os.environ['DEVICE_UDID']
        self.desired_capabilities_cloud["newCommandTimeout"] = 99999
        self.desired_capabilities_cloud["launchTimeout"] = 99999
        time.sleep(5)
        #try:
        self.driver = webdriver.Remote("http://" + os.environ['HOST'] + ":" + os.environ['UNIQUE_PORT'] + "/wd/hub", self.desired_capabilities_cloud)
        #except Exception as e:
        #log("Error Instantiating Driver [" + str(e) + "]. Attempting to continue anyway.")
        time.sleep(10)

    def postMessage(self, message):
        postString = self.jsonGridPrefix + message
        self.pubnub.publish(self.channel, postString, callback=_callback, error=error)
        log("Request posted to PubNub [" + postString + "]")

    def setChannelPrefixes(self):
        global test_run_id
        log("PUBSUB CHANNEL ID: " + self.channel)
        self.gridIdentity = "Trilleon-Automation-" + os.environ['GRID_IDENTITY_PREFIX']
        self.jsonGridPrefix = "{\"test_run_id\":\"" + test_run_id + "\"},{\"grid_identity\":\"" + self.gridIdentity + "\"},{\"grid_source\":\"server\"},{\"grid_context\":\"StandardAlert\"},"
        self.jsonBuddyGridPrefix = "{\"test_run_id\":\"" + test_run_id + "\"},{\"grid_identity\":\"" + self.buddyName + "\"},{\"grid_source\":\"server\"},{\"grid_context\":\"BuddyMessage\"},"

    # Close down the driver, call XML and JSON finalizer, close PubNub connection, and handle any fatal execution error.
    def tearDown(self):
        self.pubnub.unsubscribe(self.channel)
        self.writeAutomationResults('TEST-all.xml')
        if self.fatalErrorDetected == True:
            with open("FatalErrorDetected.txt", "w") as f:
                f.write(self.fatalErrorMessage)
        time.sleep(5)
        try:
            self.driver.quit()
        except:
            log("Error encountered while quitting driver")

        with open("test_status.txt", "w") as f:
            f.write(os.environ['TEST_RUN_STATUS'])
        pylogtext = ""
        with open("PyLog.txt", "r") as f:
            pylogtext = f.read()
        if "CRITICAL_SERVER_FAILURE_IN_APPIUM_TEST" in pylogtext:
            raise Exception("CRITICAL_SERVER_FAILURE_IN_APPIUM_TEST Detected!")

    # Writes all XML to storage file, and verifies JSON validity so that HTML report is rendered correctly.
    def writeAutomationResults(self, filename):
        # Verify that JSON recorded is valid. Add placeholder JSON if it is not.
        text = ""
        with open("testresultsjson.txt", "r") as f:
            text = f.read() # Replace any missing commas between JSON objects and attributes.
        
        if len(self.results) > 0:
            if "failure" in self.results:
                os.environ['TEST_RUN_STATUS'] = "UNSTABLE"
            else:
                os.environ['TEST_RUN_STATUS'] = "SUCCESS"
            # Game error popup check.
            if self.fatalErrorDetected:
                os.environ['TEST_RUN_STATUS'] = "CRASH_DURING_RUN"
                with open("testresultsjson.txt", "a") as f:
                    f.write("{\"order_ran\":\"999\", \"status\":\"Failed\", \"name\":\"ERROR_POPUP\", \"class\":\"FATAL_ERROR\", \"test_categories\":\"ERROR_POPUP\", \"result_details\":\"The game produced an error popup that affected automation execution. Check screenshot for details.\", \"assertions\":[]},")
            with open(os.getcwd() + "/" + filename, "w") as f:
                f.write(self.results)

        else:
            # Failure occurred - likely an app crash. Send PubNub History to help investigate where it happened.
            log("FATAL APPLICATION ERROR DISRUPTED AUTOMATION")
            # If there is existing JSON, report that app crashed or hanged.
            if len(text) > 5:
                os.environ['TEST_RUN_STATUS'] = "CRASH_DURING_RUN"
                with open("testresultsjson.txt", "a") as f:
                    f.write("{\"order_ran\":\"999\", \"status\":\"Failed\", \"name\":\"GAME_CRASHED_OR_HANGED\", \"class\":\"FATAL_ERROR\", \"test_categories\":\"CRASH_HANG\", \"result_details\":\"The game either suddenly crashed, or automation execution encountered an unhandled fatal error that halted test execution. View screenshot for details.\", \"assertions\":[]},")
            else:
                # If results were not recorded, then the app never loaded, crashed, or automation could not launch.
                if "checking_in" not in self.get_communication_history():
                    os.environ['TEST_RUN_STATUS'] = "CRASH_DURING_LAUNCH"
                    with open("testresultsjson.txt", "w") as f:
                        f.write("[{\"order_ran\":\"0\", \"status\":\"Failed\", \"name\":\"GAME_LAUNCH_FAILURE\", \"class\":\"FATAL_ERROR\", \"test_categories\":\"LAUNCH_FAILURE\", \"result_details\":\"The game crashed on load, or otherwise failed to reach a state where automation could register on the pubsub communication channel.\", \"assertions\":[]},")
                else:
                    os.environ['TEST_RUN_STATUS'] = "CRASH_AFTER_LAUNCH"
                    if "order_ran" in self.get_communication_history():
                        with open("testresultsjson.txt", "a") as f:
                            f.write(",{\"order_ran\":\"0\", \"status\":\"Failed\", \"name\":\"GAME_LAUNCH_FAILURE\", \"class\":\"FATAL_ERROR\", \"test_categories\":\"LAUNCH_FAILURE\", \"result_details\":\"The game launched, and automation registered on the pubsub communication channel, but either the app crashed, or automation was blocked from beginning its test run.\", \"assertions\":[]},")
                    else:
                        with open("testresultsjson.txt", "w") as f:
                            f.write("[{\"order_ran\":\"0\", \"status\":\"Failed\", \"name\":\"GAME_LAUNCH_FAILURE\", \"class\":\"FATAL_ERROR\", \"test_categories\":\"LAUNCH_FAILURE\", \"result_details\":\"The game launched, and automation registered on the pubsub communication channel, but either the app crashed, or automation was blocked from beginning its test run.\", \"assertions\":[]},")

    def set_screenshot_dir(self, screenshot_dir):
        log("Saving screenshots at: " + screenshot_dir)
        self.screenshot_dir = screenshot_dir
        if not os.path.exists(screenshot_dir):
            os.mkdir(self.screenshot_dir)

    def take_screenshot(self, path):
        log("Attempting to save screenshot at path [" + self.screenshot_dir + path + "]")
        try:
            self.driver.save_screenshot(self.screenshot_dir + path)
        except Exception as e:
            log("Exception! " + str(e))

    def handle_device_alert(accept):
        if os.environ['DEVICE_PLATFORM'] == "android":
            # Try to dismiss any Android account alerts.
            try:
                acceptButton = self.driver.find_element_by_xpath("//*[@text='Allow']")
                action = TouchAction(self.driver)
                action.tap(acceptButton).perform()
                log("Chose 'Allow' for Google Play alert.")
            except Exception as e:
                log("Exception accepting Android alert! " + str(e))
            try:
                acceptButton = self.driver.find_element_by_xpath("//*[@text='OK']")
                action = TouchAction(self.driver)
                action.tap(acceptButton).perform()
                log("Chose 'OK' for Google Play alert.")
            except Exception as e:
                log("Exception accepting Android alert! " + str(e))
        else:
            log("Attempting to dismiss any iOS device-level alert.")
            try:
                if accept == True:
                    self.driver.switch_to.alert.accept()
                else:
                    self.driver.switch_to.alert.dismiss()
            except Exception as e:
                log("Internal Error Accepting iOS Alert (this is not likely a problem, due to interval dismissal logic): " + str(e))

    # Write name of self to parent level file that selected Buddy will check to verify both Buddy's have begun their test runs.
    def buddy_check_in(self):
        #directoryPieces = os.getcwd().split("/")
        #self.parentDirectory = ""
        #index = len(directoryPieces)
        #for piece in directoryPieces:
            #index -= 1
            #if index > 0:
                #self.parentDirectory += piece + "/"
        #with open(self.buddyCheckFileName, "a") as f:
            #f.write(self.gridIdentity)
        log("Writing Device Identity [" + self.gridIdentity + "] To File [" + self.buddyCheckFileName + "].")

    # See if Buddy has checked in as active and running. Will be used to skip Buddy tests if associated Buddy cannot perform its function.
    def buddy_check(self):
        return True
        #fileReadTest = ""
        #with open(self.buddyCheckFileName, "r") as f:
            #fileReadTest = f.read()
        #self.buddyCheckComplete = True
        log("Reading Buddy Check File. Expecting To See Buddy Name [" + self.buddyName + "] In File Contents [" + fileReadTest + "].")
        #if self.buddyName not in fileReadTest:
            #TODO: self.postMessage("{\"buddy_ignore_all\":0}")
            #return False
        #else:
            #return True

    # Break final XML generated by AutomationReport.cs out from total pubsub history and format it properly before placing it in final xml file.
    def get_xml_from_client_run(self):
        log("Command Recieved: [Get XML]")
        global test_run_id
        recent_posts = self.get_communication_history()
        # Toke "xml_complete" is required for this script to know that a client has reached completion of its test run.
        if ("xml_complete" not in recent_posts and "completed_automation" not in recent_posts) or self.parsing_xml == True:
            return False
        self.parsing_xml = True
        log("Xml Complete token detected. Begining parsing of test results xml.")
        time.sleep(5)
        
        # Split wall of pubsub text on start token and determine how many fragments of XML our test run intends to communicate.
        splitPieces = recent_posts.split("xml_start")
        if len(splitPieces) < 2:
            log("Failed to detect xml_start token in recent posts [" + recent_posts + "]")
            return False
        rawSplit = splitPieces[1]
        delimiter = "|"
        start = rawSplit.index(delimiter) + len(delimiter)
        end = rawSplit.index(delimiter, start)
        xmlPiecesCount = int(rawSplit[start:end])

        # XML is potentially fragmented when large test runs exceed a 4000 character pubsub message limit.
        log("XML Pieces Count [" + str(xmlPiecesCount) + "]")

        # PubNub does not guarantee sequential order of messages. We know how many xml fragments to expect, but not the order they will be recieved.
        xml_parsed = [None] * xmlPiecesCount
        list_history = recent_posts.split(test_run_id)

        # Split each fragment from history and place it into a dictionary that guarantees the proper order of reconstituted xml.
        for v in list_history:
            post = v
            if "xml_fragment_" in post:
                log("XML Fragment Detected. Parsing.")
                delimiter = "||"
                start = post.find(delimiter, 0) + 2
                end = post.find(delimiter, start + 2)
                index = int(post.split("xml_fragment_")[1][0])
                xml_parsed[index] = post[start:end]
        all_xml = ""
        for p in xml_parsed:
            if p != None:
                all_xml += p
        self.results = all_xml.replace('@APOS@', '"')
        if len(all_xml) == 0:
            log("XML empty after processing. Recent posts [" + recent_posts + "]")
        log("ALL_XML_FINAL " + all_xml)
        return True
            
    # Find, extract, format, and save all single-test JSON results for server HTML report
    def get_json_from_client_run(self, force):
        log("Command Recieved: [Get JSON]")
        recent_posts = self.get_communication_history()
        if "completed_automation" in recent_posts or force == True:
            log("Generating JSON.")
            initialDelimiter = "SINGLE_TEST_RESULTS_JSON|"
            delimiter = "|"
            rawAll = recent_posts.split(initialDelimiter)
            json = ""
            index = 0
            partialInitialDelimiter = "SINGLE_TEST_RESULTS_JSON_MULTI_PART|"
            for x in rawAll:
                if index > 0 and len(x) > 0:
                    # Handle test results reporting that was too large to send in a single message, requiring several parts of a single result report.
                    if partialInitialDelimiter in x:
                        rawPartials = x.split(partialInitialDelimiter) # All partial message pieces
                        indexPartial = 0
                        piecesFinal = ["" for x in range(len(rawPartials))]
                        for z in rawPartials:
                            if indexPartial == 0:
                                json += rawPartials[0].split(delimiter)[0] # First, record the test that preceded the partial test details report.
                            else:
                                piecesPartial = z.split(self.partialDataDelimiter)
                                piecesFinal[int(piecesPartial[0])] = piecesPartial[1].split(delimiter)[0] # The first piece after splicing is the index/order of this piece. Set that piece equal to the actual message data.
                            indexPartial += 1
                        for f in piecesFinal:
                            json += f # Should piece together valid json in correct order if previous for loop correctly handled ordering.
                    else:
                        json += x.split(delimiter)[0]
                index += 1
            
            # "@APOS@" token is a special encoding of double qoutes to prevent issues with PubNub message encoding and proper formatting of JSON
            json = json.replace("@APOS@", "\"").replace("}{", "},{")
            if not json.endswith("]"):
                if json.endswith(","):
                    json = json[:-1] + "]"
                else:
                    json += "]"
            fileContent = ""
            with open("testresultsjson.txt", "r") as f:
                fileContent = f.read()
            log("JSON FINAL LENGTH [" + str(len(json)) + "]")
            try:
                log("JSON FINAL ACTUAL [" + json + "]")
            except Exception as e:
                log("Failed to parse final json [" + str(e) + "]")
            if json not in fileContent:
                with open("testresultsjson.txt", "a") as f:
                    f.write(json)
            return True
        else:
            return False

    # This is used by the server to check for client "heartbeats", or to request them, to verify that client has not crashed or hanged in execution.
    def has_heartbeat(self):
        global heartbeats
        if self.last_heartbeat_detected == None:
            self.last_heartbeat_detected = datetime.now()

        if len(heartbeats) > 0:
            log("Registered heartbeat #" + str(self.heartbeat_index))
            self.heartbeat_index += 1
            self.last_heartbeat_detected = datetime.now()

        timeDifference = (datetime.now() - self.last_heartbeat_detected).total_seconds()
        if timeDifference > self.max_time_since_heartbeat:
            self.postMessage("{\"health_check\":0}")
            log("Any heartbeat ??" + heartbeats)
            time.sleep(15)
            if len(heartbeats) > 0:
                log("Registered heartbeat #" + str(self.heartbeat_index) + " after explicit request")
                self.heartbeat_index += 1
                self.last_heartbeat_detected = datetime.now()
                return True
            else:
                log("Heartbeat not detected in expected timeframe. Time since last heartbeat [" + str(timeDifference) + " seconds]")
                return False
        else:
            return True

    # Get pubsub history saved in text file with each recieved callback.
    def get_communication_history(self):
        results = ""
        try:
            with open("RelevantPubNubCommunications.txt", "r") as f:
                results = f.read()
        except Exception as e:
            log("Exception Reading History Text: " + str(e))
        return results

    # Get only the pubsub message history that contains a provided search term.
    def get_specific_json_from_history(self, jsonFlag):
        #Return only messages bound for this server-client relationship.
        historyText = self.get_communication_history()
        if len(historyText) == 0:
            return ""
        history = historyText.split("messageFull")
        global test_run_id
        resultsString = ""
        for x in history:
            rawMessage = urllib.unquote(str(x)).replace("\\", "").replace("\"", "'").replace("+", " ")
            splitMessage = rawMessage.split("test_run_id")
            for s in splitMessage:
                if test_run_id in s and (True if jsonFlag == None else (jsonFlag in s)):
                    resultsString += s
        return resultsString

    # See if client has requested the server take a screenshot.
    def check_for_client_requests(self, command):
        if command == "screenshot":
            fileName = ""
            with open("screenshot_requests.txt", "r") as f:
                fileName = f.read().strip()
            f = open("screenshot_requests.txt", "w")
            f.write("")
            f.close()
            if any(c.isalpha() for c in fileName):
                if "Interval" in fileName:
                    fileName = str(self.auto_screenshot_index) + "_" + "interval"
                    self.auto_screenshot_index += 1
                else:
                    fileName = "/" + fileName + ".png"
                    # If this file already exists, don't take it again.
                    if os.path.exists(self.screenshot_dir + fileName) == False:
                        try:
                            self.take_screenshot(fileName)
                            self.postMessage("{\"request_response\":\"screenshot\"}")
                            log("Screenshot successfully saved [" + fileName + "]")
                        except BaseException as e:
                            log("Exception Taking Screenshot! " + str(e))
        if command == "handle_client_commands":
            commandFull = ""
            with open("client_request_queue.txt", "r") as f:
                commandFull = f.read()
            if "|" in commandFull:
                # Handle command
                commandActual = commandFull.split("|")[0]
                commandValue = commandFull.split("|")[1]
                if commandActual == "HANDLE_DEVICE_ALERT":
                    self.handle_device_alert(False if commandValue == "0" else True)
                #Clear command queue
                f = open("client_request_queue.txt", "w")
                f.write("")
                f.close()
                self.postMessage("{\"server_broker_response\":\"" + commandActual + "\"}")
            else:
                try:
                    self.driver.switch_to.alert.accept()
                except BaseException as e:
                    log("")

    def find_json_for_performance_attribute(self, delimiter):
        recent_posts = self.get_communication_history()
        endDelimiter = "|"
        json = ""
        index = 0
        fullInitialDelimiter = delimiter + "_MULTI_PART|"
        rawAll = recent_posts.split(fullInitialDelimiter)
        piecesFinal = ["" for x in range(len(rawAll) - 1)]
        log("Finding " + delimiter)
        for x in rawAll:
            if index > 0 and len(x) > 0:
                # Handle test results reporting that was too large to send in a single message, requiring several parts of a single result report.
                rawPartials = x.split(fullInitialDelimiter) # All partial message pieces
                for z in rawPartials:
                    piecesPartial = z.split(self.partialDataDelimiter)
                    if index - 1 == int(piecesPartial[0]):
                        piecesFinal[int(piecesPartial[0])] = piecesPartial[1].split(endDelimiter)[0] # The first piece after splicing is the index/order of this piece. Set that piece equal to the actual message data.
                        break
            index += 1
        for f in piecesFinal:
            json += f # Should piece together valid json in correct order if previous for loop correctly handled ordering.

        # "@APOS@" token is a special encoding of double qoutes to prevent issues with PubNub message encoding and proper formatting of JSON
        json = json.replace("@APOS@", "\"").replace("}{", "},{")
        if not json.endswith("]"):
          json += "]"
        return json

    # Check if specific messages have been communicated over PubNub and extract relevant details.
    def check_for_client_responses(self, command, postAllParsed):
        historyString = ""
        if command == "heap_json":
            log("Collecting Heap Json")
            fileWrite = open("HeapJson.txt", "w")
            fileWrite.write(self.find_json_for_performance_attribute("HEAP_JSON"))
            fileWrite.close()
            return True
        if command == "garbage_collection_json":
            log("Collecting GC Json")
            fileWrite = open("GarbageCollectionJson.txt", "w")
            fileWrite.write(self.find_json_for_performance_attribute("GC_JSON"))
            fileWrite.close()
            return True
        if command == "fps_json":
            log("Collecting FPS Json")
            fileWrite = open("FpsJson.txt", "w")
            fileWrite.write(self.find_json_for_performance_attribute("FPS_JSON"))
            fileWrite.close()
            return True
        if command == "exceptions_data":
            log("Collecting Exceptions Values")
            fileWrite = open("ExceptionsJson.txt", "w")
            fileWrite.write(self.find_json_for_performance_attribute("EXCEPTION_DATA"))
            fileWrite.close()
            return True
        if command == "garbage_collection":
            historyString = self.get_specific_json_from_history("GARBAGE_COLLECTION")
            if len(historyString) > 0:
                log("Collecting Garbage Collection Values")
                initialDelimiter = "GARBAGE_COLLECTION|"
                finalDelimiter = "|"
                raw = historyString.split(initialDelimiter)[1]
                data = raw.split(finalDelimiter)[0]
                fileWrite = open("GarbageCollection.txt", "w")
                fileWrite.write(data)
                fileWrite.close()
                return True
        if command == "heap_size":
            historyString = self.get_specific_json_from_history("HEAP_SIZE")
            if len(historyString) > 0:
                log("Collecting Heap Size Values")
                initialDelimiter = "HEAP_SIZE|"
                finalDelimiter = "|"
                raw = historyString.split(initialDelimiter)[1]
                data = raw.split(finalDelimiter)[0]
                fileWrite = open("HeapSize.txt", "w")
                fileWrite.write(data)
                fileWrite.close()
                return True
        if command == "fps":
            historyString = self.get_specific_json_from_history("FPS_VALUES")
            if len(historyString) > 0:
                log("Collecting FPS Values")
                initialDelimiter = "FPS_VALUES|"
                finalDelimiter = "|"
                raw = historyString.split(initialDelimiter)[1]
                data = raw.split(finalDelimiter)[0]
                fileWrite = open("Fps.txt", "w")
                fileWrite.write(data)
                fileWrite.close()
                return True
        if command == "game_launch_seconds":
            historyString = self.get_specific_json_from_history("GAME_LAUNCH_SECONDS")
            if len(historyString) > 0:
                log("Collecting Game Initialization Time")
                initialDelimiter = "GAME_LAUNCH_SECONDS|"
                finalDelimiter = "|"
                raw = historyString.split(initialDelimiter)[1]
                data = raw.split(finalDelimiter)[0]
                fileWrite = open("GameInitializationTime.txt", "w")
                fileWrite.write(data)
                fileWrite.close()
                return True
        if command == "device_details_html":
            historyString = self.get_specific_json_from_history("DEVICE_DETAILS_HTML")
            if len(historyString) > 0:
                log("Collecting Device Details HTML")
                initialDelimiter = "DEVICE_DETAILS_HTML|"
                finalDelimiter = "|"
                raw = historyString.split(initialDelimiter)[1]
                html = raw.split(finalDelimiter)[0].replace("@APOS@", "\"").replace("@QUOT@", "'")
                fileWrite = open("TestRunHeaderHtml.txt", "w")
                fileWrite.write(html)
                fileWrite.close()
                return True

        recent_posts = self.get_communication_history()
        if command == "ready" and ("checking_in" in recent_posts or "DBID||" in recent_posts):
             time.sleep(5)
             if "DBID||" in recent_posts:
                 dbidPiece = recent_posts.split("DBID||")[1]
                 self.DBID = dbidPiece.split("||")[0]
             return True
        if command == "started" and ("starting_automation" in recent_posts or "Starting Automation" in recent_posts or "SINGLE_TEST_RESULTS_JSON" in recent_posts):
            return True
        if command == "complete" and "completed_automation" in recent_posts:
            return True
        if command == "fatal_error_check" and "Fatal Error. Shutting down automation" in recent_posts:
            if self.fatalErrorDetected == False:
                self.fatalErrorDetected = True
                self.fatalErrorMessage = "Fatal Error popup occurred. Game Unavailable!"
                self.take_screenshot("/fatal_error.png")
            return True
        return False
예제 #18
0
class AppWindow(QtGui.QMainWindow, remote.Ui_MainWindow):
	resSlot = QtCore.pyqtSignal(str,str)
	def __init__(self, parent=None,**kwargs):
		super(AppWindow, self).__init__(parent)
		self.setupUi(self)
		self.I=kwargs.get('I',None)
		self.setWindowTitle(self.I.H.version_string+' : '+params.get('name','').replace('\n',' ') )
		self.pubEdit.setText("pub-c-22260663-a169-4935-9c74-22925f4398af")
		self.subEdit.setText("sub-c-3431f4ba-2984-11e6-a01f-0619f8945a4f")
		self.channelLabel.setText(self.I.hexid)
		self.resetKeys() #Connect to pubnub
		
		
		self.resSlot.connect(self.writeResults)
		self.thingSpeakCommand = None
		
		self.timer=QtCore.QTimer()
		self.timer.timeout.connect(self.uploadToThingSpeak)
		self.uploadToThingSpeak();
		self.timer.start(15*1e3) #15 seconds
		
		
		import inspect

		funcs=dir(self.I)
		self.methods={}
		self.function_list=[]
		for a in funcs:
			fn=getattr(self.I,a)
			try:
				args=inspect.getargspec(fn).args
			except:
				args=[]

			if len(args)>0:
				if inspect.ismethod(fn):
					self.methods[a]=(fn,args)		#list of tuples of all methods in device handler
					if args[0]=='self': self.function_list.append([a,args[1:] ])
	
	def uploadToThingSpeak(self):
		if self.thingSpeakCommand:
			try:
				result = self.thingSpeakCommand[0](*self.thingSpeakCommand[1])
				params = urllib.urlencode({'field1': float(result), 'key':str(self.thingSpeakKey.text())})
				headers = {"Content-type": "application/x-www-form-urlencoded","Accept": "text/plain"}
				conn = httplib.HTTPConnection("api.thingspeak.com:80")
				conn.request("POST", "/update", params, headers)
				response = conn.getresponse()
				self.results_2.append('%s : %s'%( response.status, response.reason))
				data = response.read()
				conn.close()
			except Exception as e:
				self.results_2.append('Error : %s'%( e.message))
				pass

	def setThingSpeakCommand(self):
		try:
			message = str(self.cmdEditThingSpeak.text())
			fn_name=message.split('(')[0]
			args = message[message.find("(")+1:message.find(")")].strip().split(',')
			total_args=[]
			for t in args:
				if not len(t):continue
				if t[0]=="'" or t[0]=='"':total_args.append(t[1:-1])
				else:total_args.append(string.atoi(t))
			
			method = self.methods.get(fn_name)[0]
			if method == None :
				print ('no such command :',fn_name)
				return 'no such command : %s'%fn_name
			else:
				#while self.hw_lock and self.active: pass
				#self.hw_lock=True
				self.thingSpeakCommand=[method,total_args]
		except Exception as e:
			self.results_2.append('Set Error : %s'%( e.message))
			pass


	def setListenState(self,state):
		if state: #Enable listen
			try:
				self.pubnub.subscribe(self.I.hexid,callback = self.callback, error=self._error, connect=self._connect, reconnect=self._reconnect, disconnect=self._disconnect)
			except Exception as e:
				self.responseLabel.setText (e)
		else:
			self.pubnub.unsubscribe(self.I.hexid)
			
	def resetKeys(self):
		try:
			from pubnub import Pubnub
			self.pubnub = Pubnub(
				publish_key = str(self.pubEdit.text()),
				subscribe_key = str(self.subEdit.text()))
		except Exception as e:
			self.responseLabel.setText (e)


	def callback(self,full_message, channel):
		msg_type = full_message[0]
		senderId = str(full_message)[1:19]
		message = str(full_message)[19:]
		try:
			if msg_type == 'Q' : #Query
				self.resSlot.emit(message,'in')		
				fn_name=message.split('(')[0]
				args = message[message.find("(")+1:message.find(")")].strip().split(',')
				total_args=[]
				for t in args:
					if not len(t):continue
					if t[0]=="'" or t[0]=='"':total_args.append(t[1:-1])
					else:total_args.append(string.atoi(t))
				
				method = self.methods.get(fn_name)[0]
				if method == None :
					print ('no such command :',fn_name)
					return 'no such command : %s'%fn_name
				else:
					#while self.hw_lock and self.active: pass
					#self.hw_lock=True
					result=method(*total_args)		
					#self.hw_lock=False
					jsonres = json.dumps(result,cls=NumpyEncoder)
					self.pubnub.publish(channel = senderId,message = 'R'+self.I.hexid+jsonres)  #R stands for response . Q for Query
					self.resSlot.emit('%s %s %s %d %s... %s'%(method.__name__,str(total_args),str(type(jsonres)),len(jsonres),str(jsonres[:20]),self.I.hexid+'response'),'out')
			elif msg_type == 'R':
				self.resSlot.emit(senderId+' > '+message,'reply')		
				
		except Exception as e:
			self.responseLabel.setText (e.message)

	def writeResults(self,txt,t):
		if t == 'in':
			self.results.append('RECV:<span style="background-color: #FFFF00">' + txt +'</span')
		elif t == 'out':
			self.results.append('SEND:<span style="background-color: #00FF00">' + txt +'</span')
		elif t == 'reply':
			self.results.append('GOT :<span style="background-color: #00FFFF">' + txt +'</span')
		elif t == 'msg':
			self.results.append('MSG :<span style="background-color: #000;color:#FFF">' + txt +'</span')


	def execRemote(self):
		chan = hex(0x1000000000000000|int(str(self.sendID.text()),16)) ; msg = str(self.cmdEdit.text())
		self.pubnub.publish(channel = chan,message = 'Q'+self.I.hexid+msg)
		self.resSlot.emit('[' + chan + ']: ' + msg,'out')


	def _connect(self,m):
		self.resSlot.emit("Connected to PubNub! Listening on "+m,'msg')

	def _reconnect(self,m):
		self.resSlot.emit("Reconnected to PubNub!",'msg')

	def _disconnect(self,m):
		self.resSlot.emit("Disconnected from PubNub!",'msg')

	def _error(self,m):
		self.resSlot.emit(" PubNub Error!",'msg')


	def __del__(self):
		try:self.pubnub.unsubscribe(self.I.hexid)
		except:pass
		try:self.pubnub.unsubscribe(self.I.hexid+'response')
		except:pass

	def closeEvent(self, event):
		try:self.pubnub.unsubscribe(self.I.hexid)
		except:pass
		try:self.pubnub.unsubscribe(self.I.hexid+'response')
		except:pass
		
		self.finished=True
예제 #19
0
class Thermostat(threading.Thread, metaclass=utils.Singleton):
    """ Main class.

    Contains decision making algorithm. Gathers all available input and data from sources.
    Maintains settings and history in memory.
    Spawns a number of daemon threads to perform background tasks.

    Notes:
        - All floating point arithmetic should use Decimal type, pass in number as str to maintain precision
    """
    def __init__(self):
        super().__init__()
        self._settings = OrderedDict(
            sorted(utils.init_settings().items(), key=lambda t: t[0]))
        self._history = utils.init_history()
        self._current_temperature = Decimal(0)
        self._temperature_range = (Decimal(0), Decimal(0))
        self._on_rpi = utils.on_rpi()
        self._mode = utils.Mode.OFF
        self._state = utils.State.IDLE

        self.logger = getLogger('app.thermostat')
        self.temperature_offset = Decimal(
            '0.8')  # DEMO value, original Decimal('1.5')
        self.last_state_update = 0

        self.cost_table = None  # must init after thread starts
        self.pubnub = Pubnub(
            publish_key=config.PUBLISH_KEY,
            subscribe_key=config.SUBSCRIBE_KEY,
            uuid=config.THERMOSTAT_ID,
        )
        self.weather_thread = weather.WeatherAPI(
            self._settings['temperature_unit'],
            self._settings['city'],
            self._settings['country_code'],
        )
        # TODO: actively record user actions (input range, changes after prediction)
        self.history_thread = threading.Timer(600, self.set_history)
        self.history_thread.daemon = True
        self.locks = {
            'settings': threading.Lock(),
            'temperature_range': threading.Lock(),
        }

    def run(self):
        """ Entry method.

        Main event loop is here.
        """
        # initialize db
        self.cost_table = sql.CostTable()

        # initialize sensor and pins
        if self.on_rpi:
            utils.init_rpi()
            sensor.init_sensor()
            self.current_temperature = sensor.read_temperature()

        # start daemon thread to fetch weather data
        self.weather_thread.start()

        # start daemon thread to record history data
        self.history_thread.start()

        # subscribe to remote access messages
        self.pubnub.subscribe(
            channels=config.THERMOSTAT_ID,
            callback=self._callback,
            error=self._error,
        )

        while True:
            if self.mode != utils.Mode.OFF:
                self.update_state()
            time.sleep(config.UPDATE_INTERVAL)

    def stop(self):
        """ Exit handler.

        Write data stored in memory back to files.
        """
        self.pubnub.unsubscribe(config.THERMOSTAT_ID)
        utils.write_to_file(config.SETTINGS_FILENAME, self._settings)
        utils.write_to_file(config.HISTORY_FILENAME, self._history)
        self.cost_table.close()
        self.logger.info('cleanup completed')

    def toggle_power(self):
        self.mode = utils.Mode.AUTO if self.mode == utils.Mode.OFF else utils.Mode.OFF
        self.state = utils.State.IDLE
        # self.last_state_update = time.time()
        self.publish_mode()

    def toggle_mode(self, mode=None):
        if isinstance(mode, utils.Mode):
            self.mode = mode
        else:
            if self.mode == utils.Mode.AUTO:
                self.mode = utils.Mode.HEAT
            elif self.mode == utils.Mode.HEAT:
                self.mode = utils.Mode.COOL
            elif self.mode == utils.Mode.COOL:
                self.mode = utils.Mode.AUTO
        self.publish_mode()

    def update_state(self):
        """ Decision maker.

        Gathers input and data to update thermostat state.
        """
        self.logger.debug('thermostat state is {0}'.format(self.state))
        if self.on_rpi:
            self.current_temperature = sensor.read_temperature()

        low, high = self.temperature_range
        if self.current_temperature < (low - self.temperature_offset):
            new_state = utils.State.HEAT
        elif self.current_temperature > (high + self.temperature_offset):
            new_state = utils.State.COOL
        else:
            # within range, make decision
            new_state = self.make_decision()

        # prevent oscillation
        if (time.time() - self.last_state_update) > config.OSCILLATION_DELAY:
            if self.state != new_state:
                self.last_state_update = time.time()

            # check mode to determine if new state is allowed
            if self.mode == utils.Mode.HEAT:
                self.state = new_state if new_state != utils.State.COOL else self.state
            elif self.mode == utils.Mode.COOL:
                self.state = new_state if new_state != utils.State.HEAT else self.state
            else:
                self.state = new_state
            self.publish_state()
            self.logger.debug('thermostat state updated to {0}'.format(
                self.state))

    def make_decision(self):
        params_list = list()

        # TODO: likely needs an additional multiplier to amplify difference
        # TODO: score is always max when this is the only parameter in DM
        rating = (self.temperature_range_equilibrium -
                  self.current_temperature) * (self.temperature_range_ceiling -
                                               self.temperature_range_floor)
        params_list.append(('internal_temperature', rating))

        # use external temperature if the data is recent
        # TODO: factor in external humidity
        if (datetime.datetime.now() -
                self.weather_thread.last_updated).total_seconds() < 3600:
            rating = (self.temperature_range_ceiling -
                      self.weather_thread.temperature) / 10
            params_list.append(('external_temperature', rating))

        # use history if the data exists
        past_temperature = self.get_history()
        if past_temperature is not None:
            rating = past_temperature - self.current_temperature
            params_list.append(('history_temperature', rating))

        # use energy cost if data exists
        cost_data = self.cost_table.select(select='start_time,cost',
                                           where={
                                               'country_code':
                                               self._settings['country_code'],
                                               'city':
                                               self._settings['city']
                                           })
        if cost_data:
            cost_data = dict(cost_data)
            lowest_cost = min(cost_data.values())
            current_hour = utils.round_time(datetime.datetime.now(), 3600).hour
            current_cost = cost_data.get(current_hour)
            if current_cost is not None:
                ratio = Decimal(lowest_cost) / Decimal(current_cost)
                rating = ratio * params_list[0][
                    1]  # ratio * (internal temperature rating)
                params_list.append(('energy_cost', rating))

        matrix = decision.build_decision_matrix(params_list)
        return decision.evaluate_decision_matrix(matrix)

    def get_setting(self, name):
        """ Get setting value.

        :param name: setting name
        :return: setting value, None if setting does not exist
        """
        return self._settings.get(name)

    def set_setting(self, name, value):
        """ Set setting value.

        :param name: setting name
        :param value: setting value
        :return: None
        """
        if name in self._settings:
            self._settings[name] = value

    def get_history(self, dt=None):
        """ Get temperature at specified datetime.

        If dt is None, defaults to current time. Full datetime is needed to evaluate day of week.

        :param dt: datetime.datetime object
        :return: temperature
        """
        if dt is None:
            dt = datetime.datetime.now()
        elif not isinstance(dt, datetime.datetime):
            self.logger.error('must be datetime object')
            return None

        rounded_dt = utils.round_time(dt)
        day = utils.WeekDay(rounded_dt.weekday()).name
        time_block = rounded_dt.strftime('%H:%M')
        temperature = self._history[day][time_block]
        return Decimal(temperature) if temperature is not None else None

    def set_history(self, dt=None, temperature=None):
        """ Set temperature at specified datetime.

        If dt is None, defaults to current time. Full datetime is need to evaluate day of week.
        If temperature is None, defaults to current temperature.

        :param dt: datetime.datetime object
        :param temperature: temperature to record
        :return: None
        """
        if temperature is None:
            temperature = self.current_temperature

        if dt is None:
            dt = datetime.datetime.now()
        elif not isinstance(dt, datetime.datetime):
            self.logger.error('must be datetime object')
            return None

        rounded_dt = utils.round_time(dt)
        day = utils.WeekDay(rounded_dt.weekday()).name
        time_block = rounded_dt.strftime('%H:%M')
        self._history[day][time_block] = str(
            temperature)  # store as str to avoid conversion to JSON float

    def publish_temperatures(self, current=True, range=True):
        data = {
            'action': 'temperature_data',
            'data': {
                'current_temperature':
                round(self.current_temperature) if current else None,
                'temperature_low':
                round(self.temperature_range_floor) if range else None,
                'temperature_high':
                round(self.temperature_range_ceiling) if range else None,
            }
        }
        self.pubnub.publish(config.THERMOSTAT_ID, data, error=self._error)
        self.logger.debug('published message: {0}'.format(data))

    def publish_mode(self):
        data = {
            'action': 'mode_data',
            'data': {
                'mode': str(self.mode).lower()
            }
        }
        self.pubnub.publish(config.THERMOSTAT_ID, data, error=self._error)
        self.logger.debug('published message: {0}'.format(data))

    def publish_state(self):
        data = {
            'action': 'state_data',
            'data': {
                'state': str(self.state).lower()
            }
        }
        self.pubnub.publish(config.THERMOSTAT_ID, data, error=self._error)
        self.logger.debug('published message: {0}'.format(data))

    def publish_settings(self):
        data = {
            'action': 'settings_data',
            'data': utils.prettify_settings(self.settings)
        }
        self.pubnub.publish(config.THERMOSTAT_ID, data, error=self._error)
        self.logger.debug('published message: {0}'.format(data))

    def publish_history(self):
        data = {
            'action': 'history_data',
            'data': utils.get_history_graph_data_webapp(self._history)
        }
        self.pubnub.publish(config.THERMOSTAT_ID, data, error=self._error)

    def _callback(self, message, channel):
        """ Pubnub message callback.

        :param message: received payload
        :param channel: channel name
        :return: None
        """
        self.logger.debug(message)

        if message['action'] == 'request_temperatures':
            self.publish_temperatures(range=(message['value'] == 'all'))
        elif message['action'] == 'request_mode':
            self.publish_mode()
        elif message['action'] == 'request_settings':
            self.publish_settings()
        elif message['action'] == 'request_history':
            self.publish_history()
        elif message['action'] == 'update_temperature_range':
            low = message.get('temperature_low')
            high = message.get('temperature_high')
            if low is not None and high is not None:
                self.temperature_range = (Decimal(low), Decimal(high))
        elif message['action'] == 'update_mode':
            mode = message.get('mode')
            if mode is not None:
                mode = utils.Mode[mode.upper()]
                self.toggle_mode(mode)
        elif message['action'] == 'update_setting':
            name = message.get('setting_name')
            value = message.get('setting_value')
            if name is not None and value is not None:
                self.settings = utils.unprettify_setting_name(
                    self.settings, name, value)

    def _error(self, message):
        """ Pubnub error callback.

        :param message:
        :return: None
        """
        self.logger.error(message)

    @staticmethod
    def validate_temperature(value):
        if value < config.MIN_TEMPERATURE:
            raise errors.TemperatureValidationError(
                'Temperature cannot be below {}'.format(
                    config.MIN_TEMPERATURE))
        if value > config.MAX_TEMPERATURE:
            raise errors.TemperatureValidationError(
                'Temperature cannot be above {}'.format(
                    config.MAX_TEMPERATURE))

    @property
    def on_rpi(self):
        return self._on_rpi

    @property
    def is_on(self):
        return self.mode != utils.Mode.OFF

    @property
    def is_active(self):
        return self.state != utils.State.IDLE

    @property
    def mode(self):
        return self._mode

    @mode.setter
    def mode(self, mode):
        if self.on_rpi and mode == utils.Mode.OFF:
            GPIO.output(config.FAN_PIN, config.RELAY_OFF)
            GPIO.output(config.HEAT_PIN, config.RELAY_OFF)
            GPIO.output(config.COOL_PIN, config.RELAY_OFF)
        self._mode = mode

    @property
    def state(self):
        return self._state

    @state.setter
    def state(self, state):
        if self.on_rpi:
            if state == utils.State.IDLE:
                GPIO.output(config.FAN_PIN, config.RELAY_OFF)
                GPIO.output(config.HEAT_PIN, config.RELAY_OFF)
                GPIO.output(config.COOL_PIN, config.RELAY_OFF)
            elif state == utils.State.HEAT:
                GPIO.output(config.FAN_PIN, config.RELAY_ON)
                GPIO.output(config.HEAT_PIN, config.RELAY_ON)
                GPIO.output(config.COOL_PIN, config.RELAY_OFF)
            elif state == utils.State.COOL:
                GPIO.output(config.FAN_PIN, config.RELAY_ON)
                GPIO.output(config.HEAT_PIN, config.RELAY_OFF)
                GPIO.output(config.COOL_PIN, config.RELAY_ON)
        self._state = state

    @property
    def settings(self):
        return self._settings

    @settings.setter
    def settings(self, t):
        # TODO: validation
        self.locks['settings'].acquire(False)
        key, value = t
        if self._settings.get(key):
            # TODO: make country code changeable
            if key == 'city':
                self.weather_thread.location = (
                    value, self.weather_thread._location['country_code'])
            self._settings[key] = value
            self.publish_settings()
        self.locks['settings'].release()

    @property
    def current_temperature(self):
        return self._current_temperature

    @current_temperature.setter
    def current_temperature(self, value):
        self._current_temperature = value

    @property
    def temperature_range(self):
        return self._temperature_range

    @temperature_range.setter
    def temperature_range(self, t_range):
        self.locks['temperature_range'].acquire(False)
        try:
            low, high = t_range
            if high < low:
                raise errors.TemperatureValidationError('Invalid range.')
            self.validate_temperature(low)
            self.validate_temperature(high)
            self._temperature_range = t_range
            # send update
            self.publish_temperatures(current=False)
        finally:
            self.locks['temperature_range'].release()

    @property
    def temperature_range_floor(self):
        return min(self.temperature_range)

    @property
    def temperature_range_ceiling(self):
        return max(self.temperature_range)

    @property
    def temperature_range_equilibrium(self):
        # DEMO value
        return self.temperature_range_ceiling

        # biased towards range ceiling
        low, high = self.temperature_range
        bias = (high - low) / 4
        equilibrium = sum(self.temperature_range) / 2
        return equilibrium + bias
예제 #20
0
class Logs(object):
    """
    This class implements functions that allow processing logs from device.

    This class is implemented using pubnub python sdk.

    For more details about pubnub, please visit: https://www.pubnub.com/docs/python/pubnub-python-sdk

    """
    def __init__(self):
        self.config = Config()

    def _init_pubnub(func):
        @wraps(func)
        def wrapper(self, *args, **kwargs):
            if not hasattr(self, 'pubnub'):
                pubnub_key = self.config.get_all()['pubnub']
                self.pubnub = Pubnub(publish_key=pubnub_key['publish_key'],
                                     subscribe_key=pubnub_key['subscribe_key'])
            return func(self, *args, **kwargs)

        return wrapper

    @_init_pubnub
    def subscribe(self, uuid, callback, error):
        """
        This function allows subscribing to device logs.
        Testing

        Args:
            uuid (str): device uuid.
            callback (function): this callback is called on receiving a message from the channel.
            error (function): this callback is called on an error event.
            For more details about callbacks in pubnub subscribe, visit here: https://www.pubnub.com/docs/python/api-reference#subscribe

        Examples:
            >>> def callback(message, channel):
            ... print(message)

            >>> def error(message):
            ... print('Error:'+ str(message))

            >>> Logs.subscribe(uuid=uuid, callback=callback, error=error)

        """

        channel = self.get_channel(uuid)
        self.pubnub.subscribe(channels=channel, callback=callback, error=error)

    @_init_pubnub
    def history(self, uuid, callback, error):
        """
        This function allows fetching historical device logs.

        Args:
            uuid (str): device uuid.
            callback (function): this callback is called on receiving a message from the channel.
            error (function): this callback is called on an error event.
            For more details about callbacks in pubnub subscribe, visit here: https://www.pubnub.com/docs/python/api-reference#history

        Examples:
            >>> def callback(message):
            ... print(message)

            >>> def error(message):
            ... print('Error:'+ str(message))

            Logs.history(uuid=uuid, callback=callback, error=error)

        """

        channel = self.get_channel(uuid)
        self.pubnub.history(channel=channel, callback=callback, error=error)

    def unsubscribe(self, uuid):
        """
        This function allows unsubscribing to device logs.

        Args:
            uuid (str): device uuid.

        """

        if hasattr(self, 'pubnub'):
            channel = self.get_channel(uuid)
            self.pubnub.unsubscribe(channel=channel)

    @staticmethod
    def get_channel(uuid):
        """
        This function returns pubnub channel for a specific device.

        Args:
            uuid (str): device uuid.

        Returns:
            str: device channel.

        """

        return 'device-{uuid}-logs'.format(uuid=uuid)
예제 #21
0
class manageSteeds(Thread):

    test = os.environ
    pubnub_publish_key = os.environ['PUBNUB_PUBLISH_KEY']
    pubnub_subscribe_key = os.environ['PUBNUB_SUBSCRIBE_KEY']

    mongodb_connection = None
    collection = None

    def __init__(self):
        Thread.__init__(self)
        mongodb_connection = ConnectionToDatabase()

        self.steeds_collection = mongodb_connection.getCollection(
            "available_steeds")
        self.deliveries_collection = mongodb_connection.getCollection(
            "temp_deliveries")
        self.deliveries_steeds_collection = mongodb_connection.getCollection(
            "temp_deliveries_steeds")

        self.pubnub_settings = Pubnub(
            publish_key=manageSteeds.pubnub_publish_key,
            subscribe_key=manageSteeds.pubnub_subscribe_key)
        # Rename to location channel
        self.pubnub_channel = "steeds_channel"

        self.genericDAO = GenericDAO()

        self.scheduler = sched.scheduler()  # Instansiate a scheduler

    def subscriber_callback(self, message, channel):
        x = 1

    def subscriber_error(self, message):
        print("ERROR : " + message)

    def connect(self, message):
        print("CONNECTED TO STEEDS CHANNEL")

    def reconnect(self, message):
        print("RECONNECTED TO STEEDS CHANNEL")

    def disconnect(self, message):
        print("DISCONNECTED FROM STEEDS CHANNEL")

    def subscribe(self):
        # souscrire au channel
        self.pubnub_settings.subscribe(channels=self.pubnub_channel,
                                       callback=self.subscriber_callback,
                                       error=self.subscriber_error,
                                       connect=self.connect,
                                       reconnect=self.reconnect,
                                       disconnect=self.disconnect)

    def publish(self, message):
        self.pubnub_settings.publish(channel=self.pubnub_channel,
                                     message=message)

    def unsubscribe(self):

        # se desinscire du channel
        self.pubnub_settings.unsubscribe(self.pubnub_channel)
# how to instansiate a scheduler

#Out of Pubnub functions

    def manageClientDeliveryRequest(self, iddelivery):

        # Search delivery by id (criteria)
        delivery_criteria = {}
        delivery_criteria["_id"] = iddelivery

        temp_delivery = self.genericDAO.getOneObject(
            self, self.deliveries_colllection, delivery_criteria)

        #Extract pickup coordinates from temporary delivery
        client_coordinates = [
            temp_delivery["pickup_lng"], temp_delivery["pickup_lat"]
        ]

        # Get nearest steeds to pickup location
        nearest_steeds = self.genericDAO.getNearestSteeds(
            self, self.steeds_collection, client_coordinates)

        # Save available steeds for delivery
        delivery_steeds = {}
        delivery_steeds["iddelivery"] = iddelivery
        delivery_steeds["available_steeds"] = nearest_steeds

        self.genericDAO.insertObject(self.deliveries_steeds_collection,
                                     delivery_steeds)

        #Send delivery request to seeder
        self.sendDeliveryRequestToSteed(iddelivery)

    def sendDeliveryRequestToSteed(self, iddelivery):

        # Search delivery by id (criteria)
        delivery_criteria = {}
        delivery_criteria["_id"] = iddelivery

        temp_delivery = self.genericDAO.getOneObject(
            self, self.deliveries_colllection, delivery_criteria)

        #Check received_positive_response field
        received_response = temp_delivery["received_positive_response"]

        if (not (received_response)):

            #Search available steeds for delivery
            delivery_steeds_criteria = {}
            delivery_steeds_criteria["iddelivery"] = iddelivery

            available_steeds = self.genericDAO.getOneObject(
                self, self.deliveries_steeds_collection,
                delivery_steeds_criteria)

            #Send steed delivery request
            self.publish("XXXX" + iddelivery + available_steeds[0]["_id"] +
                         " " + "Other delivery details")

            #Delete 1st steed from available steeds list
            new_available_steeds = available_steeds[1:len(available_steeds) -
                                                    1]

            #Update delivery available steeds

            update_search_criteria = {}
            update_search_criteria["iddelivery"] = iddelivery

            update_field = {}
            update_field["available_steeds"] = new_available_steeds

            #Add update function to Generic DAO from old projects

            #Schedule method call
            self.scheduler.enter(10, 1,
                                 self.sendDeliveryRequestToSteed(iddelivery))
            self.scheduler.run()