From 1fd573e3b2fbc5f45b25be61292a2b5c1e5cad48 Mon Sep 17 00:00:00 2001 From: Nicola Peditto Date: Tue, 6 Feb 2018 11:17:39 +0100 Subject: [PATCH] Moved to Python 3.5 Code moved from Python 2.7 to 3.5. Substituted Twisted module with Asyncio. Change-Id: I926a48ba2f6ce8b8c0ec43592105f864c4d0c7dc --- etc/iotronic/iotronic.conf | 1 + iotronic_lightningrod/Board.py | 13 +- iotronic_lightningrod/common/exception.py | 2 + iotronic_lightningrod/config.py | 4 +- iotronic_lightningrod/devices/Device.py | 2 +- iotronic_lightningrod/devices/gpio/Gpio.py | 1 + iotronic_lightningrod/devices/gpio/server.py | 1 + iotronic_lightningrod/devices/gpio/yun.py | 5 +- iotronic_lightningrod/devices/server.py | 9 +- iotronic_lightningrod/devices/yun.py | 24 +- iotronic_lightningrod/lightningrod.py | 740 +++++++++--------- iotronic_lightningrod/modules/Module.py | 2 +- .../modules/device_manager.py | 5 +- .../modules/plugin_manager.py | 153 ++-- iotronic_lightningrod/modules/test.py | 17 +- iotronic_lightningrod/modules/utils.py | 37 +- iotronic_lightningrod/modules/vfs_library.py | 2 + iotronic_lightningrod/modules/vfs_manager.py | 18 +- iotronic_lightningrod/plugins/Plugin.py | 1 + .../plugins/PluginSerializer.py | 10 +- iotronic_lightningrod/plugins/pluginApis.py | 2 + .../{ => arduino_yun}/demo.py | 140 ++-- .../plugins_examples/arduino_yun/led.json | 3 + .../plugins_examples/arduino_yun/led.py | 37 + .../{ => generics}/runner.json | 0 .../plugins_examples/{ => generics}/runner.py | 1 + .../plugins_examples/generics/zero.json | 1 + .../plugins_examples/{ => generics}/zero.py | 0 .../plugins/plugins_examples/smartme.json | 13 - .../plugins/plugins_examples/smartme.py | 409 ---------- iotronic_lightningrod/wampmessage.py | 2 + requirements.txt | 12 +- setup.cfg | 7 +- tox.ini | 36 +- 34 files changed, 682 insertions(+), 1028 deletions(-) rename iotronic_lightningrod/plugins/plugins_examples/{ => arduino_yun}/demo.py (96%) create mode 100644 iotronic_lightningrod/plugins/plugins_examples/arduino_yun/led.json create mode 100644 iotronic_lightningrod/plugins/plugins_examples/arduino_yun/led.py rename iotronic_lightningrod/plugins/plugins_examples/{ => generics}/runner.json (100%) rename iotronic_lightningrod/plugins/plugins_examples/{ => generics}/runner.py (94%) create mode 100644 iotronic_lightningrod/plugins/plugins_examples/generics/zero.json rename iotronic_lightningrod/plugins/plugins_examples/{ => generics}/zero.py (100%) delete mode 100644 iotronic_lightningrod/plugins/plugins_examples/smartme.json delete mode 100644 iotronic_lightningrod/plugins/plugins_examples/smartme.py diff --git a/etc/iotronic/iotronic.conf b/etc/iotronic/iotronic.conf index b4ee0b0..94e4ef1 100644 --- a/etc/iotronic/iotronic.conf +++ b/etc/iotronic/iotronic.conf @@ -1,3 +1,4 @@ [DEFAULT] debug = True log_file = /var/log/iotronic/lightning-rod.log +lightningrod_home = /var/lib/iotronic diff --git a/iotronic_lightningrod/Board.py b/iotronic_lightningrod/Board.py index ac2e878..47b568a 100644 --- a/iotronic_lightningrod/Board.py +++ b/iotronic_lightningrod/Board.py @@ -13,17 +13,19 @@ # License for the specific language governing permissions and limitations # under the License. +__author__ = "Nicola Peditto " + str(meth[0])) - # LOG.info(" --> " + str(rpc_addr)) def modulesLoader(session): @@ -169,8 +182,7 @@ def modulesLoader(session): LOG.info("\n\nListening...") -@inlineCallbacks -def IotronicLogin(board, session, details): +async def IotronicLogin(board, session, details): """Function called to connect the board to Iotronic. The board: @@ -187,18 +199,16 @@ def IotronicLogin(board, session, details): global reconnection - global SESSION - SESSION = session - try: rpc = str(board.agent) + u'.stack4things.connection' with timeoutRPC(seconds=3, action=rpc): - res = yield session.call(rpc, - uuid=board.uuid, - session=details.session - ) + res = await session.call( + rpc, + uuid=board.uuid, + session=details.session + ) w_msg = WM.deserialize(res) @@ -209,14 +219,14 @@ def IotronicLogin(board, session, details): # LOADING BOARD MODULES try: - yield modulesLoader(session) + modulesLoader(session) except Exception as e: LOG.warning("WARNING - Could not register procedures: " + str(e)) # Reset flag to False - reconnection = False + # reconnection = False else: LOG.error(" - Access denied to Iotronic.") @@ -228,324 +238,16 @@ def IotronicLogin(board, session, details): # the "stack4things.connection" RPC. # The board will disconnect from WAMP agent and retry later. reconnection = True - session.disconnect() + + # We stop the Component in order to trigger the onDisconnect event + component.stop() except Exception as e: LOG.warning("Iotronic board connection error: " + str(e)) -class WampFrontend(ApplicationSession): - """Function to manage the WAMP connection events. - - """ - - @inlineCallbacks - def onJoin(self, details): - """Execute the following procedures when the board connects to WAMP server. - - :param details: WAMP session details - - """ - - # LIGHTNING-ROD STATE: - # - REGISTRATION STATE: the first connection to Iotronic - # - FIRST CONNECTION: the board become operative after registration - # - LIGHTNING-ROD BOOT: the first connection to WAMP - # after Lightning-rod starting - # - WAMP RECOVERY: when the established WAMP connection fails - - global reconnection - - # reconnection flag is False when the board is: - # - LIGHTNING-ROD BOOT - # - REGISTRATION STATE - # - FIRST CONNECTION - # - # reconnection flag is True when the board is: - # - WAMP RECOVERY - - global SESSION - SESSION = self - - # LOG.debug(" - session: " + str(details)) - - board.session = self - board.session_id = details.session - - LOG.info(" - Joined in realm " + board.wamp_config['realm'] + ":") - LOG.info(" - WAMP Agent: " + str(board.agent)) - LOG.info(" - Session ID: " + str(details.session)) - - if reconnection is False: - - if board.uuid is None: - - ###################### - # REGISTRATION STATE # - ###################### - - # If in the LR configuration file there is not the Board UUID - # specified it means the board is a new one and it has to call - # IoTronic in order to complete the registration. - - try: - - LOG.info(" - Board needs to be registered to Iotronic.") - - rpc = u'stack4things.register' - - with timeoutRPC(seconds=3, action=rpc): - res = yield self.call( - rpc, - code=board.code, - session=details.session - ) - - w_msg = WM.deserialize(res) - - # LOG.info(" - Board registration result: \n" + - # json.loads(w_msg.message, indent=4)) - - if w_msg.result == WM.SUCCESS: - - LOG.info("Registration authorized by Iotronic:\n" - + str(w_msg.message)) - - # the 'message' field contains - # the board configuration to load - board.setConf(w_msg.message) - - # We need to disconnect the client from the - # registration-agent inorder to reconnect - # to the WAMP agent assigned by Iotronic - # at the provisioning stage - LOG.info("\n\nDisconnecting from Registration Agent " - "to load new settings...\n\n") - self.disconnect() - - else: - LOG.error("Registration denied by Iotronic: " - + str(w_msg.message)) - Bye() - - except exception.ApplicationError as e: - LOG.error("IoTronic registration error: " + str(e)) - # Iotronic is offline the board can not call the - # "stack4things.connection" RPC. - # The board will disconnect from WAMP agent and retry later - - # TO ACTIVE BOOT CONNECTION RECOVERY MODE - reconnection = True - self.disconnect() - - except Exception as e: - LOG.warning(" - Board registration call error: " + str(e)) - Bye() - - else: - - if board.status == "registered": - #################### - # FIRST CONNECTION # - #################### - - # In this case we manage the first reconnection - # after the registration stage: - # Lightining-rod sets its status to "operative" - # completing the provisioning and configuration stage. - LOG.info("\n\n\nBoard is becoming operative...\n\n\n") - board.updateStatus("operative") - board.loadSettings() - IotronicLogin(board, self, details) - - elif board.status == "operative": - ###################### - # LIGHTNING-ROD BOOT # - ###################### - - # After join to WAMP agent, Lightning-rod will: - # - authenticate to Iotronic - # - load the enabled modules - - # The board will keep at this tage until it will succeed - # to connect to Iotronic. - IotronicLogin(board, self, details) - - else: - LOG.error("Wrong board status '" + board.status + "'.") - Bye() - - else: - - ################# - # WAMP RECOVERY # - ################# - - LOG.info("IoTronic connection recovery:") - - try: - - rpc = str(board.agent) + u'.stack4things.connection' - - with timeoutRPC(seconds=3, action=rpc): - res = yield self.call( - rpc, - uuid=board.uuid, - session=details.session - ) - - w_msg = WM.deserialize(res) - - if w_msg.result == WM.SUCCESS: - - LOG.info(" - Access granted to Iotronic.") - - # LOADING BOARD MODULES - # If the board is in WAMP connection recovery state - # we need to register again the RPCs of each module - try: - - yield moduleReloadInfo(self) - - # Reset flag to False - reconnection = False - - LOG.info("WAMP Session Recovered!") - - LOG.info("\n\nListening...\n\n") - - except Exception as e: - LOG.warning("WARNING - Could not register procedures: " - + str(e)) - Bye() - - else: - LOG.error("Access to IoTronic denied: " - + str(w_msg.message)) - Bye() - - except exception.ApplicationError as e: - LOG.error("IoTronic connection error: " + str(e)) - # Iotronic is offline the board can not call - # the "stack4things.connection" RPC. - # The board will disconnect from WAMP agent and retry later - - # TO ACTIVE WAMP CONNECTION RECOVERY MODE - reconnection = False - self.disconnect() - - except Exception as e: - LOG.warning("Board connection error after WAMP recovery: " - + str(e)) - Bye() - - @inlineCallbacks - def onLeave(self, details): - LOG.warning('WAMP Session Left: ' + str(details)) - - -class WampClientFactory(websocket.WampWebSocketClientFactory, - ReconnectingClientFactory): - - def clientConnectionFailed(self, connector, reason): - """Procedure triggered on WAMP connection failure. - - :param connector: WAMP connector object - :param reason: WAMP connection failure reason - - """ - LOG.warning("WAMP Connection Failed: Crossbar server unreachable.") - ReconnectingClientFactory.clientConnectionFailed( - self, - connector, - reason - ) - - def clientConnectionLost(self, connector, reason): - """Procedure triggered on WAMP connection lost. - - :param connector: WAMP connector object - :param reason: WAMP connection failure reason - - """ - - LOG.warning("WAMP Connection Lost.") - - global reconnection - - LOG.warning("WAMP status: board = " + str(board.status) - + " - reconnection = " + str(reconnection)) - - if board.status == "operative" and reconnection is False: - - ################# - # WAMP RECOVERY # - ################# - - # we need to recover wamp session and - # we set reconnection flag to True in order to activate - # the RPCs module registration procedure for each module - - reconnection = True - - LOG.info("Reconnecting to " + str(connector.getDestination().host) - + ":" + str(connector.getDestination().port)) - - ReconnectingClientFactory.clientConnectionLost( - self, - connector, - reason - ) - - elif board.status == "operative" and reconnection is True: - - ###################### - # LIGHTNING-ROD BOOT # - ###################### - - # At this stage if the reconnection flag was set to True - # it means that we forced the reconnection procedure - # because of the board is not able to connect to IoTronic - # calling "stack4things.connection" RPC... - # it means IoTronic is offline! - - # We need to reset the recconnection flag to False in order to - # do not enter in RPCs module registration procedure... - # At this stage the board tries to reconnect to - # IoTronic until it will come online again. - reconnection = False - - LOG.info("Connecting to " + str(connector.getDestination().host) - + ":" + str(connector.getDestination().port)) - - ReconnectingClientFactory.clientConnectionLost( - self, - connector, - reason - ) - - elif (board.status == "registered"): - ###################### - # REGISTRATION STATE # - ###################### - - # LR was disconnected from Registration Agent - # in order to connect it to the assigned WAMP Agent. - - LOG.debug("\n\nReconnecting after registration...\n\n") - - # LR load the new configuration and gets the new WAMP Agent - board.loadSettings() - - # LR has to connect to the assigned WAMP Agent - wampConnect(board.wamp_config) - - else: - LOG.error("Reconnection wrong status!") - - def wampConnect(wamp_conf): - """WAMP connection procedure. + """WAMP connection procedures. :param wamp_conf: WAMP configuration from settings.json file @@ -555,32 +257,326 @@ def wampConnect(wamp_conf): try: - component_config = types.ComponentConfig( - realm=unicode(wamp_conf['realm']) + LOG.info("WAMP status:" + + "\n- board = " + str(board.status) + + "\n- reconnection = " + str(reconnection) + + "\n- connection = " + str(connected) + ) + + # LR creates the Autobahn Asyncio Component that points to the + # WAMP Agent (main/registration agent) + global component + component = Component( + transports=wamp_conf['url'], + realm=wamp_conf['realm'] ) - session_factory = wamp.ApplicationSessionFactory( - config=component_config - ) - session_factory.session = WampFrontend - transport_factory = WampClientFactory( - session_factory, - url=wamp_conf['url'] - ) - transport_factory.autoPingInterval = 5 - transport_factory.autoPingTimeout = 5 + # To manage the registration stage: we got the info for the main + # WAMP agent and LR is going to connect to it starting the Component + # with the new WAMP configuration. + if connected == False and board.status == "registered" \ + and reconnection == False: + component.start(loop) - connector = websocket.connectWS(transport_factory) + @component.on_join + async def join(session, details): + """Execute the following procedures when the board connects + to Crossbar. - try: + :param details: WAMP session details - addr = str(connector.getDestination().host) - socket.inet_pton(socket.AF_INET, addr) - LOG.info(" - establishing connection to : " + str(addr)) + """ - except socket.error as err: - LOG.error(" - IP address validation error: " + str(err)) - Bye() + global connected + connected = True + + # LIGHTNING-ROD STATES: + # - REGISTRATION STATE: the first connection to Iotronic + # - FIRST CONNECTION: the board become operative after registration + # - LIGHTNING-ROD BOOT: the first connection to WAMP + # after Lightning-rod starting + # - WAMP RECOVERY: when the established WAMP connection fails + + global reconnection + + # reconnection flag is False when the board is: + # - LIGHTNING-ROD BOOT + # - REGISTRATION STATE + # - FIRST CONNECTION + # + # reconnection flag is True when the board is: + # - WAMP RECOVERY + + global SESSION + SESSION = session + + # LOG.debug(" - session: " + str(details)) + + board.session_id = details.session + + LOG.info(" - Joined in realm " + board.wamp_config['realm'] + ":") + LOG.info(" - WAMP Agent: " + str(board.agent)) + LOG.info(" - Session ID: " + str(board.session_id)) + LOG.info(" - Board status: " + str(board.status)) + + if reconnection is False: + + if board.uuid is None: + + ###################### + # REGISTRATION STATE # + ###################### + # If in the LR configuration file there is not the + # Board UUID specified it means the board is a new one + # and it has to call IoTronic in order to complete + # the registration. + + try: + + LOG.info(" - Board needs to be registered.") + + rpc = u'stack4things.register' + + with timeoutRPC(seconds=3, action=rpc): + res = await session.call( + rpc, + code=board.code, + session=board.session_id + ) + + w_msg = WM.deserialize(res) + + # LOG.info(" - Board registration result: \n" + + # json.loads(w_msg.message, indent=4)) + + if w_msg.result == WM.SUCCESS: + + LOG.info("Registration authorized by IoTronic:\n" + + str(w_msg.message)) + + # the 'message' field contains + # the board configuration to load + board.setConf(w_msg.message) + + # We need to disconnect the client from the + # registration-agent in order to reconnect + # to the WAMP agent assigned by Iotronic + # at the provisioning stage + LOG.info( + "\n\nDisconnecting from Registration Agent " + "to load new settings...\n\n") + + # We stop the Component in order to trigger the + # onDisconnect event + component.stop() + + else: + LOG.error("Registration denied by Iotronic: " + + str(w_msg.message)) + Bye() + + except exception.ApplicationError as e: + LOG.error("IoTronic registration error: " + str(e)) + # Iotronic is offline the board can not call the + # "stack4things.connection" RPC. The board will + # disconnect from WAMP agent and retry later. + + # TO ACTIVE BOOT CONNECTION RECOVERY MODE + reconnection = True + # We stop the Component in order to trigger the + # onDisconnect event + component.stop() + + except Exception as e: + LOG.warning( + " - Board registration call error: " + str(e)) + Bye() + + else: + + if board.status == "registered": + #################### + # FIRST CONNECTION # + #################### + + # In this case we manage the first connection + # after the registration stage: + # Lightining-rod sets its status to "operative" + # completing the provisioning and configuration stage. + LOG.info("\n\n\nBoard is becoming operative...\n\n\n") + board.updateStatus("operative") + board.loadSettings() + LOG.info("WAMP status:" + + "\n- board = " + str(board.status) + + "\n- reconnection = " + str(reconnection) + + "\n- connection = " + str(connected) + ) + await IotronicLogin(board, session, details) + + elif board.status == "operative": + ###################### + # LIGHTNING-ROD BOOT # + ###################### + + # After join to WAMP agent, Lightning-rod will: + # - authenticate to Iotronic + # - load the enabled modules + + # The board will keep at this stage until + # it will succeed to connect to Iotronic. + await IotronicLogin(board, session, details) + + else: + LOG.error("Wrong board status '" + board.status + "'.") + Bye() + + else: + + ################# + # WAMP RECOVERY # + ################# + + LOG.info("IoTronic connection recovery:") + + try: + + rpc = str(board.agent) + u'.stack4things.connection' + + with timeoutRPC(seconds=3, action=rpc): + res = await session.call( + rpc, + uuid=board.uuid, + session=details.session + ) + + w_msg = WM.deserialize(res) + + if w_msg.result == WM.SUCCESS: + + LOG.info(" - Access granted to Iotronic.") + + # LOADING BOARD MODULES + # If the board is in WAMP connection recovery state + # we need to register again the RPCs of each module + try: + + moduleReloadInfo(session) + + # Reset flag to False + reconnection = False + + LOG.info("WAMP Session Recovered!") + + LOG.info("\n\nListening...\n\n") + + except Exception as e: + LOG.warning( + "WARNING - Could not register procedures: " + + str(e)) + Bye() + + else: + LOG.error("Access to IoTronic denied: " + + str(w_msg.message)) + Bye() + + except exception.ApplicationError as e: + LOG.error("IoTronic connection error: " + str(e)) + # Iotronic is offline the board can not call + # the "stack4things.connection" RPC. + # The board will disconnect from WAMP agent and retry later + + # TO ACTIVE WAMP CONNECTION RECOVERY MODE + reconnection = False + # We stop the Component in order to trigger the + # onDisconnect event + component.stop() + + except Exception as e: + LOG.warning("Board connection error after WAMP recovery: " + + str(e)) + Bye() + + @component.on_leave + async def onLeave(session, details): + LOG.warning('WAMP Session Left: ' + str(details)) + + @component.on_disconnect + async def onDisconnect(session, was_clean): + """Procedure triggered on WAMP connection lost, for istance + when we call component.stop(). + + :param connector: WAMP connector object + :param reason: WAMP connection failure reason + + """ + + LOG.warning('WAMP Transport Left: was_clean = ' + str(was_clean)) + global connected + connected = False + + global reconnection + + LOG.info("WAMP status:" + + "\n- board = " + str(board.status) + + "\n- reconnection = " + str(reconnection) + + "\n- connection = " + str(connected) + ) + + if board.status == "operative" and reconnection is False: + + ################# + # WAMP RECOVERY # + ################# + # we need to recover wamp session and + # we set reconnection flag to True in order to activate + # the RPCs module registration procedure for each module + + reconnection = True + + # LR needs to reconncet to WAMP + if not connected: + component.start(loop) + + elif board.status == "operative" and reconnection is True: + + ###################### + # LIGHTNING-ROD BOOT # + ###################### + # At this stage if the reconnection flag was set to True + # it means that we forced the reconnection procedure + # because of the board is not able to connect to IoTronic + # calling "stack4things.connection" RPC... + # it means IoTronic is offline! + + # We need to reset the recconnection flag to False in order to + # do not enter in RPCs module registration procedure... + # At this stage the board tries to reconnect to + # IoTronic until it will come online again. + reconnection = False + + # LR needs to reconncet to WAMP + if not connected: + component.start(loop) + + elif (board.status == "registered"): + ###################### + # REGISTRATION STATE # + ###################### + + # LR was disconnected from Registration Agent + # in order to connect it to the assigned WAMP Agent. + + LOG.debug("\n\nReconnecting after registration...\n\n") + + # LR load the new configuration and gets the new WAMP Agent + board.loadSettings() + + # LR has to connect to the assigned WAMP Agent + wampConnect(board.wamp_config) + + else: + LOG.error("Reconnection wrong status!") except Exception as err: LOG.error(" - URI validation error: " + str(err)) @@ -594,34 +590,21 @@ class WampManager(object): def __init__(self, wamp_conf): - # Connection to Crossbar server. + # wampConnect configures and manages the connection to Crossbar server. wampConnect(wamp_conf) def start(self): LOG.info(" - starting Lightning-rod WAMP server...") - reactor.run() - """ - # TEMPORARY ------------------------------------------------------ - from subprocess import call - LOG.debug("Unmounting...") - - try: - mountPoint = "/opt/BBB" - # errorCode = self.libc.umount(mountPoint, None) - errorCode = call(["umount", "-l", mountPoint]) - - LOG.debug("Unmount " + mountPoint + " result: " + str(errorCode)) - - except Exception as msg: - result = "Unmounting error:", msg - LOG.debug(result) - # ------------------------------------------------------------------ - """ + global loop + loop = asyncio.get_event_loop() + component.start(loop) + loop.run_forever() def stop(self): LOG.info("Stopping WAMP agent server...") - reactor.stop() + # Canceling pending tasks and stopping the loop + asyncio.gather(*asyncio.Task.all_tasks()).cancel() LOG.info("WAMP server stopped!") @@ -646,6 +629,9 @@ class LightningRod(object): CONF(project='iotronic') logging.setup(CONF, DOMAIN) + if CONF.debug: + txaio.start_logging(level="debug") + signal.signal(signal.SIGINT, self.stop_handler) LogoLR() @@ -664,9 +650,7 @@ class LightningRod(object): def stop_handler(self, signum, frame): LOG.info("LR is shutting down...") - self.w.stop() - Bye() diff --git a/iotronic_lightningrod/modules/Module.py b/iotronic_lightningrod/modules/Module.py index bd59c44..ca57c9a 100644 --- a/iotronic_lightningrod/modules/Module.py +++ b/iotronic_lightningrod/modules/Module.py @@ -13,7 +13,7 @@ # License for the specific language governing permissions and limitations # under the License. -__author__ = "MDSLAB Team" +__author__ = "Nicola Peditto " + str(rpc_addr)) - SESSION.register(inlineCallbacks(meth[1]), rpc_addr) + SESSION.register(meth[1], rpc_addr) LOG.info(" --> " + str(meth[0]) + " registered!") diff --git a/iotronic_lightningrod/modules/plugin_manager.py b/iotronic_lightningrod/modules/plugin_manager.py index 41fd366..582ea3e 100644 --- a/iotronic_lightningrod/modules/plugin_manager.py +++ b/iotronic_lightningrod/modules/plugin_manager.py @@ -12,30 +12,31 @@ # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. - from __future__ import absolute_import +__author__ = "Nicola Peditto create JSON body for the CKAN request - :return: ckan_data --> JSON data to send as request body to CKAN - - """ - - # Call Sensors Metrics: Temperature(), etc... - m_value = str(globals()[metric.capitalize()]()) - - m_timestamp = datetime.datetime.now().strftime('%Y-%m-%dT%H:%M:%S.%f') - - if metric == 'noise': - LOG.info("Noise: " + str(m_value) + " amplitude") - - elif metric == 'temperature': - LOG.info("Temperature " + str(m_value) + u" \u2103") - - if ckan: - - ckan_data = {} - ckan_data["resource_id"] = str(SENSORS[metric]['ckanID']) - ckan_data["method"] = "insert" - ckan_data["records"] = [] - sample = {} - sample["Latitude"] = location['latitude'] - sample["Longitude"] = location['longitude'] - sample["Altitude"] = location['altitude'] - metric_func_name = metric.capitalize() - sample[metric_func_name] = m_value - sample["Date"] = m_timestamp - ckan_data["records"].append(sample) - - ckan_data = json.dumps(ckan_data) - - else: - ckan_data = None - - return ckan_data - - -def getCKANdataset(board_uuid): - """To get CKAN resource IDs for each metric type managed by SmartME boards. - - :param board_uuid: - :return: - - """ - - datasets_url = "http://" + ckan_addr + "/api/rest/dataset/" + board_uuid - datasets = API.sendRequest(url=datasets_url, action='GET') - ckan_data = json.loads(datasets) - - for resource in ckan_data['resources']: - - # LOG.info(resource['name'].capitalize()) - - if resource['name'] in sensors_list: - # LOG.debug(resource['name']) - SENSORS[resource['name']]['ckanID'] = resource['id'] - # LOG.info(resource['name'] + " - " + resource['id']) - - -def setSensorsLayout(params): - for sensor in sensors_list: - SENSORS[sensor] = {} - SENSORS[sensor]['pin'] = params[sensor]['pin'] - SENSORS[sensor]['enabled'] = params[sensor]['enabled'] - - -def InitSmartMeBoard(params): - """This function init the SmartME board. - - In the SmartME Arduino YUN board this function enables the needed - devices and set the needed parameters about sensors and location. - - :param params: plugin parameters to configure the board. - - """ - - # get location - global location - location = API.getLocation() - LOG.info( - "Board location: \n" - + json.dumps(location, indent=4, separators=(',', ': ')) - ) - - # set devices - try: - - device.EnableI2c() - device.EnableGPIO() - - except Exception as err: - LOG.error("Error configuring devices: " + str(err)) - global THR_KILL - THR_KILL = False - - # set up sensors - setSensorsLayout(params) - - -class Worker(Plugin.Plugin): - - def __init__(self, uuid, name, q_result=None, params=None): - super(Worker, self).__init__( - uuid, name, - q_result=q_result, - params=params - ) - - def run(self): - - LOG.info("SmartME plugin starting...") - - global THR_KILL - THR_KILL = self._is_running - - # Board initialization - LOG.info("PARAMS list: " + str(self.params.keys())) - - if len(self.params.keys()) != 0: - - InitSmartMeBoard(self.params) - - # Get polling time - polling_time = float(self.params['polling']) - LOG.info("Polling time: " + str(polling_time)) - - # GET CKAN SENSORS UUID - getCKANdataset(API.getBoardID()) - - LOG.info( - "SENSORS: \n" - + json.dumps(SENSORS, indent=4, separators=(',', ': ')) - ) - - # START NOISE LISTENER if sensor enabled - if SENSORS['noise']['enabled']: - LOG.info("Starting noise listening...") - noise_listner() - - LOG.info("CKAN enabled: " + str(self.params['ckan_enabled'])) - - counter = 0 - - while (self._is_running and THR_KILL): - - if sensors_list.__len__() != 0: - - LOG.info("\n\n") - - for sensor in sensors_list: - - if SENSORS[sensor]['enabled']: - - if self.params['ckan_enabled']: - - API.sendRequest( - url=action_URL, - action='POST', - headers=headers, - body=getMetric(sensor, ckan=True), - verbose=False - ) - - else: - getMetric(sensor, ckan=False) - - counter = counter + 1 - LOG.info("Samples sent: " + str(counter)) - - time.sleep(polling_time) - - else: - LOG.warning("No sensors!") - self._is_running = False - THR_KILL = self._is_running - - # Update the thread status: at this stage THR_KILL will be False - THR_KILL = self._is_running - - else: - LOG.error("No parameters provided!") diff --git a/iotronic_lightningrod/wampmessage.py b/iotronic_lightningrod/wampmessage.py index 07c0878..12b58db 100644 --- a/iotronic_lightningrod/wampmessage.py +++ b/iotronic_lightningrod/wampmessage.py @@ -13,6 +13,8 @@ # License for the specific language governing permissions and limitations # under the License. +__author__ = "Nicola Peditto =2.0.0 # Apache-2.0 +pbr>=2.0.0,!=2.1.0 # Apache-2.0 +autobahn>=0.10.1 # MIT License +six>=1.10.0 # MIT +httplib2>=0.9.1 # MIT # Openstack modules -oslo.config>=3.22.0 # Apache-2.0 -oslo.log>=3.22.0 # Apache-2.0 - -autobahn>=0.10.1 # MIT License -httplib2>=0.7.5 # MIT \ No newline at end of file +oslo.config>=5.1.0 # Apache-2.0 +oslo.log>=3.36.0 # Apache-2.0 diff --git a/setup.cfg b/setup.cfg index f5388ab..f921f2a 100644 --- a/setup.cfg +++ b/setup.cfg @@ -16,8 +16,7 @@ classifier = Programming Language :: Python :: 2 Programming Language :: Python :: 2.7 Programming Language :: Python :: 3 - Programming Language :: Python :: 3.3 - Programming Language :: Python :: 3.4 + Programming Language :: Python :: 3.5 [files] packages = @@ -58,3 +57,7 @@ s4t.modules = utility = iotronic_lightningrod.modules.utils:Utility plugin = iotronic_lightningrod.modules.plugin_manager:PluginManager device = iotronic_lightningrod.modules.device_manager:DeviceManager + +[options] +build_scripts = +executable= /usr/bin/env python diff --git a/tox.ini b/tox.ini index 1238925..53c37ea 100644 --- a/tox.ini +++ b/tox.ini @@ -1,18 +1,25 @@ [tox] -minversion = 2.0 -envlist = py35,py27,pypy,pep8 +minversion = 2.3.1 +envlist = py35,pep8 skipsdist = True [testenv] -usedevelop = True -install_command = pip install -c{env:UPPER_CONSTRAINTS_FILE:https://git.openstack.org/cgit/openstack/requirements/plain/upper-constraints.txt} {opts} {packages} setenv = VIRTUAL_ENV={envdir} PYTHONWARNINGS=default::DeprecationWarning + LANGUAGE=en_US + LC_ALL=en_US.utf-8 +whitelist_externals = bash + find + rm +usedevelop = True +install_command = pip install -c{env:UPPER_CONSTRAINTS_FILE:https://git.openstack.org/cgit/openstack/requirements/plain/upper-constraints.txt} {opts} {packages} deps = -r{toxinidir}/test-requirements.txt -commands = find . -type f -name "*.pyc" -delete +commands = + find . -type f -name "*.pyc" -delete [testenv:pep8] +basepython = python2.7 commands = flake8 {posargs} [testenv:venv] @@ -22,19 +29,26 @@ commands = {posargs} commands = python setup.py test --coverage --testr-args='{posargs}' [testenv:docs] -commands = python setup.py build_sphinx +commands = + rm -fr doc/build + python setup.py build_sphinx [testenv:releasenotes] commands = sphinx-build -a -E -W -d releasenotes/build/doctrees -b html releasenotes/source releasenotes/build/html -[testenv:debug] -commands = oslo_debug_helper {posargs} +[testenv:py35] +basepython = python3.5 + [flake8] +# TODO(dmllr): Analyze or fix the warnings blacklisted below +# E711 comparison to None should be 'if cond is not None:' +# E712 comparison to True should be 'if cond is True:' or 'if cond:' +# H404 multi line docstring should start with a summary +# H405 multi line docstring summary not separated with an empty line # E123, E125 skipped as they are invalid PEP-8. - show-source = True -ignore = E123,E125 builtins = _ -exclude=.venv,.git,.tox,dist,doc,*lib/python*,*egg,build \ No newline at end of file +ignore = E711,E712,H404,H405,E123,E125,E901 +exclude = .venv,.git,.tox,dist,doc,etc,*lib/python*,*egg,build,iotronic_lightningrod/plugins/plugins_examples/ \ No newline at end of file