Moved to Python 3.5

Code moved from Python 2.7 to 3.5. Substituted Twisted module with Asyncio.

Change-Id: I926a48ba2f6ce8b8c0ec43592105f864c4d0c7dc
This commit is contained in:
Nicola Peditto 2018-02-06 11:17:39 +01:00
parent f953021d76
commit 1fd573e3b2
34 changed files with 682 additions and 1028 deletions

View File

@ -1,3 +1,4 @@
[DEFAULT]
debug = True
log_file = /var/log/iotronic/lightning-rod.log
lightningrod_home = /var/lib/iotronic

View File

@ -13,17 +13,19 @@
# License for the specific language governing permissions and limitations
# under the License.
__author__ = "Nicola Peditto <npeditto@unime.it"
from datetime import datetime
# from dateutil.tz import tzlocal
import json
import os
from iotronic_lightningrod.config import iotronic_home
from oslo_config import cfg
from oslo_log import log as logging
LOG = logging.getLogger(__name__)
SETTINGS = iotronic_home + '/settings.json'
CONF = cfg.CONF
SETTINGS = '/etc/iotronic/settings.json'
class Board(object):
@ -74,6 +76,9 @@ class Board(object):
'''
LOG.info("Lightning-rod home:")
LOG.info(" - " + CONF.lightningrod_home)
# Load all settings.json file
self.iotronic_config = self.loadConf()

View File

@ -13,6 +13,8 @@
# License for the specific language governing permissions and limitations
# under the License.
__author__ = "Nicola Peditto <npeditto@unime.it"
import os
import signal

View File

@ -13,6 +13,7 @@
# License for the specific language governing permissions and limitations
# under the License.
__author__ = "Nicola Peditto <npeditto@unime.it"
import os
import pkg_resources
@ -23,6 +24,3 @@ entry_points_name = \
# Iotronic python package folder
package_path = os.path.join(dist.location, __package__)
# Iotronic home folder
iotronic_home = "/var/lib/iotronic"

View File

@ -13,7 +13,7 @@
# License for the specific language governing permissions and limitations
# under the License.
__author__ = "MDSLAB Team"
__author__ = "Nicola Peditto <npeditto@unime.it"
import abc
import six

View File

@ -13,6 +13,7 @@
# License for the specific language governing permissions and limitations
# under the License.
__author__ = "Nicola Peditto <npeditto@unime.it"
import abc
import six

View File

@ -13,6 +13,7 @@
# License for the specific language governing permissions and limitations
# under the License.
__author__ = "Nicola Peditto <npeditto@unime.it"
from oslo_log import log as logging

View File

@ -13,10 +13,13 @@
# License for the specific language governing permissions and limitations
# under the License.
from iotronic_lightningrod.devices.gpio import Gpio
__author__ = "Nicola Peditto <npeditto@unime.it"
import os
import time
from iotronic_lightningrod.devices.gpio import Gpio
from oslo_log import log as logging
LOG = logging.getLogger(__name__)

View File

@ -13,8 +13,9 @@
# License for the specific language governing permissions and limitations
# under the License.
__author__ = "Nicola Peditto <npeditto@unime.it"
import inspect
from twisted.internet.defer import returnValue
from iotronic_lightningrod.devices import Device
from iotronic_lightningrod.devices.gpio import server
@ -46,10 +47,10 @@ class System(Device.Device):
"""
pass
def testRPC(self):
async def testRPC(self):
rpc_name = whoami()
LOG.info("RPC " + rpc_name + " CALLED...")
yield makeNothing()
await makeNothing()
result = " - " + rpc_name + " result: testRPC is working!!!\n"
LOG.info(result)
returnValue(result)
return result

View File

@ -13,9 +13,7 @@
# License for the specific language governing permissions and limitations
# under the License.
# Linino references: http://wiki.linino.org/doku.php?id=wiki:lininoio_sysfs
from twisted.internet.defer import returnValue
__author__ = "Nicola Peditto <npeditto@unime.it"
from iotronic_lightningrod.devices import Device
from iotronic_lightningrod.devices.gpio import yun
@ -23,6 +21,8 @@ from iotronic_lightningrod.devices.gpio import yun
from oslo_log import log as logging
LOG = logging.getLogger(__name__)
# Linino references: http://wiki.linino.org/doku.php?id=wiki:lininoio_sysfs
class System(Device.Device):
@ -41,25 +41,25 @@ class System(Device.Device):
"""
pass
def testLED(self):
async def testLED(self):
LOG.info(" - testLED CALLED...")
yield self.gpio.blinkLed()
await self.gpio.blinkLed()
result = "testLED: LED blinking!\n"
LOG.info(result)
returnValue(result)
return result
def setGPIOs(self, Dpin, direction, value):
async def setGPIOs(self, Dpin, direction, value):
LOG.info(" - setGPIOs CALLED... digital pin " + Dpin
+ " (GPIO n. " + self.gpio.MAPPING[Dpin] + ")")
result = yield self.gpio._setGPIOs(Dpin, direction, value)
result = await self.gpio._setGPIOs(Dpin, direction, value)
LOG.info(result)
returnValue(result)
return result
def readVoltage(self, Apin):
async def readVoltage(self, Apin):
"""To read the voltage applied on the pin A0,A1,A2,A3,A4,A5
"""
@ -67,6 +67,6 @@ class System(Device.Device):
voltage = self.gpio._readVoltage(Apin)
result = yield "read voltage for " + Apin + " pin: " + voltage
result = await "read voltage for " + Apin + " pin: " + voltage
LOG.info(result)
returnValue(result)
return result

View File

@ -1,5 +1,4 @@
# Copyright 2017 MDSLAB - University of Messina
# All Rights Reserved.
# Copyright 2017 MDSLAB - University of Messina All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
@ -13,16 +12,13 @@
# License for the specific language governing permissions and limitations
# under the License.
__author__ = "Nicola Peditto <npeditto@unime.it"
# Autobahn and Twisted imports
from autobahn.twisted import wamp
from autobahn.twisted.wamp import ApplicationSession
from autobahn.twisted import websocket
# Autobahn imports
import asyncio
from autobahn.asyncio.component import Component
from autobahn.wamp import exception
from autobahn.wamp import types
from twisted.internet.defer import inlineCallbacks
from twisted.internet.protocol import ReconnectingClientFactory
from twisted.internet import reactor
import txaio
# OSLO imports
from oslo_config import cfg
@ -33,7 +29,6 @@ import inspect
import os
import pkg_resources
import signal
import socket
from stevedore import extension
import sys
@ -46,7 +41,17 @@ import iotronic_lightningrod.wampmessage as WM
# Global variables
LOG = logging.getLogger(__name__)
lr_opts = [
cfg.StrOpt('lightningrod_home',
default='/var/lib/iotronic',
help=('Lightning Home Data')),
]
CONF = cfg.CONF
CONF.register_opts(lr_opts)
SESSION = None
global board
board = None
@ -54,6 +59,13 @@ reconnection = False
RPC = {}
RPC_devices = {}
# ASYNCIO
loop = None
component = None
txaio.start_logging(level="info")
RUNNER = None
connected = False
def moduleReloadInfo(session):
"""This function is used in the reconnection stage to register
@ -101,9 +113,10 @@ def moduleWampRegister(session, meth_list):
# We don't considere the __init__ and finalize methods
if (meth[0] != "__init__") & (meth[0] != "finalize"):
rpc_addr = u'iotronic.' + board.uuid + '.' + meth[0]
session.register(inlineCallbacks(meth[1]), rpc_addr)
session.register(meth[1], rpc_addr)
LOG.info(" --> " + 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()

View File

@ -13,7 +13,7 @@
# License for the specific language governing permissions and limitations
# under the License.
__author__ = "MDSLAB Team"
__author__ = "Nicola Peditto <npeditto@unime.it"
import abc
import six

View File

@ -13,10 +13,11 @@
# License for the specific language governing permissions and limitations
# under the License.
__author__ = "Nicola Peditto <npeditto@unime.it"
import imp
import inspect
import os
from twisted.internet.defer import inlineCallbacks
from iotronic_lightningrod.config import package_path
from iotronic_lightningrod.lightningrod import RPC_devices
@ -39,7 +40,7 @@ def deviceWampRegister(dev_meth_list, board):
# LOG.info(" - " + str(meth[0]))
rpc_addr = u'iotronic.' + board.uuid + '.' + meth[0]
# LOG.debug(" --> " + str(rpc_addr))
SESSION.register(inlineCallbacks(meth[1]), rpc_addr)
SESSION.register(meth[1], rpc_addr)
LOG.info(" --> " + str(meth[0]) + " registered!")

View File

@ -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 <npeditto@unime.it"
from datetime import datetime
import imp
import inspect
import json
import os
from Queue import Queue
import queue
import shutil
import time
from twisted.internet.defer import returnValue
from iotronic_lightningrod.config import iotronic_home
from iotronic_lightningrod.modules import Module
from iotronic_lightningrod.plugins import PluginSerializer
import iotronic_lightningrod.wampmessage as WM
from oslo_config import cfg
from oslo_log import log as logging
LOG = logging.getLogger(__name__)
CONF = cfg.CONF
PLUGINS_THRS = {}
PLUGINS_CONF_FILE = iotronic_home + "/plugins.json"
PLUGINS_CONF_FILE = CONF.lightningrod_home + "/plugins.json"
def getFuncName():
@ -144,7 +145,8 @@ def RebootOnBootPlugins():
LOG.info(" - Rebooting plugin " + plugin_uuid)
plugin_home = iotronic_home + "/plugins/" + plugin_uuid
plugin_home = CONF.lightningrod_home + "/plugins/" \
+ plugin_uuid
plugin_filename = plugin_home + "/" + plugin_uuid + ".py"
plugin_params_file = \
plugin_home + "/" + plugin_uuid + ".json"
@ -224,7 +226,7 @@ class PluginManager(Module.Module):
# Reboot boot enabled plugins
RebootOnBootPlugins()
def PluginInject(self, plugin, onboot):
async def PluginInject(self, plugin, onboot):
"""Plugin injection procedure into the board:
1. get Plugin files
@ -254,7 +256,8 @@ class PluginManager(Module.Module):
loaded = ser.deserialize_entity(code)
# LOG.debug("- plugin loaded code:\n" + loaded)
plugin_path = iotronic_home + "/plugins/" + plugin_uuid + "/"
plugin_path = CONF.lightningrod_home \
+ "/plugins/" + plugin_uuid + "/"
plugin_filename = plugin_path + plugin_uuid + ".py"
# Plugin folder creation if does not exist
@ -311,17 +314,18 @@ class PluginManager(Module.Module):
json.dump(plugins_conf, f, indent=4)
LOG.info(" - " + message)
w_msg = yield WM.WampSuccess(message)
w_msg = await WM.WampSuccess(message)
except Exception as err:
message = "Plugin injection error: " + str(err)
LOG.error(" - " + message)
w_msg = yield WM.WampError(str(err))
returnValue(w_msg.serialize())
w_msg = await WM.WampError(str(err))
returnValue(w_msg.serialize())
return w_msg.serialize()
def PluginStart(self, plugin_uuid, parameters=None):
return w_msg.serialize()
async def PluginStart(self, plugin_uuid, parameters=None):
"""To start an asynchronous plugin;
the plugin will run until the PluginStop is called.
@ -352,12 +356,12 @@ class PluginManager(Module.Module):
message = "ALREADY STARTED!"
LOG.warning(" - Plugin "
+ plugin_uuid + " already started!")
w_msg = yield WM.WampError(message)
w_msg = await WM.WampError(message)
else:
plugin_home = \
iotronic_home + "/plugins/" + plugin_uuid
CONF.lightningrod_home + "/plugins/" + plugin_uuid
plugin_filename = \
plugin_home + "/" + plugin_uuid + ".py"
plugin_params_file = \
@ -404,32 +408,33 @@ class PluginManager(Module.Module):
response = "STARTED"
LOG.info(" - " + worker.complete(rpc_name, response))
w_msg = yield WM.WampSuccess(response)
w_msg = await WM.WampSuccess(response)
else:
message = \
rpc_name + " - ERROR " \
+ plugin_filename + " does not exist!"
LOG.error(" - " + message)
w_msg = yield WM.WampError(message)
w_msg = await WM.WampError(message)
else:
message = "Plugin " + plugin_uuid \
+ " does not exist in this board!"
LOG.warning(" - " + message)
w_msg = yield WM.WampError(message)
w_msg = await WM.WampError(message)
except Exception as err:
message = \
rpc_name + " - ERROR - plugin (" + plugin_uuid + ") - " \
+ str(err)
LOG.error(" - " + message)
w_msg = yield WM.WampError(str(err))
returnValue(w_msg.serialize())
w_msg = await WM.WampError(str(err))
returnValue(w_msg.serialize())
return w_msg.serialize()
def PluginStop(self, plugin_uuid, parameters=None):
return w_msg.serialize()
async def PluginStop(self, plugin_uuid, parameters=None):
"""To stop an asynchronous plugin
:param plugin_uuid: ID of plufin to stop
@ -459,13 +464,13 @@ class PluginManager(Module.Module):
if 'delay' in parameters:
time.sleep(delay)
yield worker.stop()
await worker.stop()
del PLUGINS_THRS[plugin_uuid]
message = "STOPPED"
LOG.info(" - " + worker.complete(rpc_name, message))
w_msg = yield WM.WampSuccess(message)
w_msg = await WM.WampSuccess(message)
else:
message = \
@ -473,26 +478,27 @@ class PluginManager(Module.Module):
+ " - ERROR - plugin (" + plugin_uuid \
+ ") is instantiated but is not running anymore!"
LOG.error(" - " + message)
w_msg = yield WM.WampError(message)
w_msg = await WM.WampError(message)
else:
message = \
rpc_name + " - WARNING " \
+ plugin_uuid + " is not running!"
LOG.warning(" - " + message)
w_msg = yield WM.WampWarning(message)
w_msg = await WM.WampWarning(message)
except Exception as err:
message = \
rpc_name \
+ " - ERROR - plugin (" + plugin_uuid + ") - " + str(err)
LOG.error(" - " + message)
w_msg = yield WM.WampError(str(err))
returnValue(w_msg.serialize())
w_msg = await WM.WampError(str(err))
returnValue(w_msg.serialize())
return w_msg.serialize()
def PluginCall(self, plugin_uuid, parameters=None):
return w_msg.serialize()
async def PluginCall(self, plugin_uuid, parameters=None):
"""To execute a synchronous plugin into the board
:param plugin_uuid:
@ -512,11 +518,12 @@ class PluginManager(Module.Module):
message = "Plugin " + plugin_uuid + " already started!"
LOG.warning(" - " + message)
w_msg = yield WM.WampWarning(message)
w_msg = WM.WampWarning(message)
else:
plugin_home = iotronic_home + "/plugins/" + plugin_uuid
plugin_home = CONF.lightningrod_home \
+ "/plugins/" + plugin_uuid
plugin_filename = plugin_home + "/" + plugin_uuid + ".py"
plugin_params_file = plugin_home + "/" + plugin_uuid + ".json"
@ -532,14 +539,15 @@ class PluginManager(Module.Module):
LOG.info(" - Plugin " + plugin_uuid + " imported!")
q_result = Queue()
q_result = queue.Queue()
except Exception as err:
message = "Error importing plugin " \
+ plugin_filename + ": " + str(err)
LOG.error(" - " + message)
w_msg = yield WM.WampError(str(err))
returnValue(w_msg.serialize())
w_msg = WM.WampError(str(err))
return w_msg.serialize()
try:
@ -575,33 +583,35 @@ class PluginManager(Module.Module):
response = q_result.get()
LOG.info(" - " + worker.complete(rpc_name, response))
w_msg = yield WM.WampSuccess(response)
w_msg = WM.WampSuccess(response)
except Exception as err:
message = "Error spawning plugin " \
+ plugin_filename + ": " + str(err)
LOG.error(" - " + message)
w_msg = yield WM.WampError(str(err))
returnValue(w_msg.serialize())
w_msg = WM.WampError(str(err))
return w_msg.serialize()
else:
message = \
rpc_name \
+ " - ERROR " + plugin_filename + " does not exist!"
LOG.error(" - " + message)
w_msg = yield WM.WampError(message)
w_msg = WM.WampError(message)
except Exception as err:
message = \
rpc_name \
+ " - ERROR - plugin (" + plugin_uuid + ") - " + str(err)
LOG.error(" - " + message)
w_msg = yield WM.WampError(str(err))
returnValue(w_msg.serialize())
w_msg = WM.WampError(str(err))
returnValue(w_msg.serialize())
return w_msg.serialize()
def PluginRemove(self, plugin_uuid):
return w_msg.serialize()
async def PluginRemove(self, plugin_uuid):
"""To remove a plugin from the board
:param plugin_uuid:
@ -613,15 +623,16 @@ class PluginManager(Module.Module):
LOG.info("RPC " + rpc_name + " for plugin " + plugin_uuid)
plugin_path = iotronic_home + "/plugins/" + plugin_uuid + "/"
plugin_path = CONF.lightningrod_home + "/plugins/" + plugin_uuid + "/"
if os.path.exists(plugin_path) is False \
or os.path.exists(PLUGINS_CONF_FILE) is False:
message = "Plugin paths or files do not exist!"
LOG.error(message)
w_msg = yield WM.WampError(message)
returnValue(w_msg.serialize())
w_msg = await WM.WampError(message)
return w_msg.serialize()
else:
@ -641,8 +652,9 @@ class PluginManager(Module.Module):
message = "Removing plugin's files error in " \
+ plugin_path + ": " + str(err)
LOG.error(" - " + message)
w_msg = yield WM.WampError(str(err))
returnValue(w_msg.serialize())
w_msg = await WM.WampError(str(err))
return w_msg.serialize()
# Remove from plugins.json file its configuration
try:
@ -678,22 +690,25 @@ class PluginManager(Module.Module):
+ plugin_uuid + " already removed!"
LOG.warning(" - " + message)
w_msg = yield WM.WampSuccess(message)
returnValue(w_msg.serialize())
w_msg = await WM.WampSuccess(message)
return w_msg.serialize()
except Exception as err:
message = "Updating plugins.json error: " + str(err)
LOG.error(" - " + message)
w_msg = yield WM.WampError(str(err))
returnValue(w_msg.serialize())
w_msg = await WM.WampError(str(err))
return w_msg.serialize()
except Exception as err:
message = "Plugin removing error: {0}".format(err)
LOG.error(" - " + message)
w_msg = yield WM.WampError(str(err))
returnValue(w_msg.serialize())
w_msg = await WM.WampError(str(err))
def PluginReboot(self, plugin_uuid, parameters=None):
return w_msg.serialize()
async def PluginReboot(self, plugin_uuid, parameters=None):
"""To reboot an asynchronous plugin (callable = false) into the board.
:return: return a response to RPC request
@ -710,7 +725,7 @@ class PluginManager(Module.Module):
try:
plugin_home = iotronic_home + "/plugins/" + plugin_uuid
plugin_home = CONF.lightningrod_home + "/plugins/" + plugin_uuid
plugin_filename = plugin_home + "/" + plugin_uuid + ".py"
plugin_params_file = plugin_home + "/" + plugin_uuid + ".json"
@ -774,23 +789,24 @@ class PluginManager(Module.Module):
message = "REBOOTED"
LOG.info(" - " + worker.complete(rpc_name, message))
w_msg = yield WM.WampSuccess(message)
w_msg = await WM.WampSuccess(message)
else:
message = "ERROR '" + plugin_filename + "' does not exist!"
LOG.error(" - " + message)
w_msg = yield WM.WampError(message)
w_msg = await WM.WampError(message)
except Exception as err:
message = "Error rebooting plugin '" \
+ plugin_uuid + "': " + str(err)
LOG.error(" - " + message)
w_msg = yield WM.WampError(str(err))
returnValue(w_msg.serialize())
w_msg = await WM.WampError(str(err))
returnValue(w_msg.serialize())
return w_msg.serialize()
def PluginStatus(self, plugin_uuid):
return w_msg.serialize()
async def PluginStatus(self, plugin_uuid):
"""Check status thread plugin
:param plugin_uuid:
@ -814,20 +830,21 @@ class PluginManager(Module.Module):
result = "DEAD"
LOG.info(" - " + worker.complete(rpc_name, result))
w_msg = yield WM.WampSuccess(result)
w_msg = await WM.WampSuccess(result)
else:
result = "DEAD"
LOG.info(" - " + rpc_name + " result for "
+ plugin_uuid + ": " + result)
w_msg = yield WM.WampSuccess(result)
w_msg = await WM.WampSuccess(result)
except Exception as err:
message = \
rpc_name \
+ " - ERROR - plugin (" + plugin_uuid + ") - " + str(err)
LOG.error(" - " + message)
w_msg = yield WM.WampError(str(err))
returnValue(w_msg.serialize())
w_msg = await WM.WampError(str(err))
returnValue(w_msg.serialize())
return w_msg.serialize()
return w_msg.serialize()

View File

@ -13,10 +13,11 @@
# License for the specific language governing permissions and limitations
# under the License.
__author__ = "Nicola Peditto <npeditto@unime.it"
import asyncio
from autobahn.twisted.util import sleep
from iotronic_lightningrod.modules import Module
from twisted.internet.defer import returnValue
from oslo_log import log as logging
LOG = logging.getLogger(__name__)
@ -28,15 +29,15 @@ class Test(Module.Module):
super(Test, self).__init__("Test", board)
def test_function(self):
async def test_function(self):
import random
s = random.uniform(0.5, 1.5)
yield sleep(s)
await asyncio.sleep(s)
result = "DEVICE test result: TEST!"
LOG.info(result)
returnValue(result)
return result
def add(self, x, y):
c = yield x + y
async def add(self, x, y):
c = await x + y
LOG.info("DEVICE add result: " + str(c))
returnValue(c)
return c

View File

@ -13,22 +13,21 @@
# License for the specific language governing permissions and limitations
# under the License.
__author__ = "Nicola Peditto <npeditto@unime.it"
from autobahn.twisted.util import sleep
from iotronic_lightningrod.config import entry_points_name
from iotronic_lightningrod.modules import Module
import asyncio
import pkg_resources
from six import moves
from stevedore import extension
import sys
from twisted.internet.defer import returnValue
from iotronic_lightningrod.config import entry_points_name
from iotronic_lightningrod.lightningrod import SESSION
from iotronic_lightningrod.modules import Module
from oslo_log import log as logging
LOG = logging.getLogger(__name__)
from iotronic_lightningrod.lightningrod import SESSION
def refresh_stevedore(namespace=None):
"""Trigger reload of entry points.
@ -62,17 +61,17 @@ class Utility(Module.Module):
def finalize(self):
pass
def hello(self, client_name, message):
async def hello(self, client_name, message):
import random
s = random.uniform(0.5, 3.0)
yield sleep(s)
await asyncio.sleep(s)
result = "Hello by board to Conductor " + client_name + \
" that said me " + message + " - Time: " + '%.2f' % s
LOG.info("DEVICE hello result: " + str(result))
returnValue(result)
return result
def plug_and_play(self, new_module, new_class):
async def plug_and_play(self, new_module, new_class):
LOG.info("LR modules loaded:\n\t" + new_module)
# Updating entry_points
@ -92,28 +91,28 @@ class Utility(Module.Module):
for ep in pkg_resources.iter_entry_points(group='s4t.modules'):
named_objects.update({ep.name: ep.load()})
yield named_objects
await named_objects
SESSION.disconnect()
returnValue(str(named_objects))
return str(named_objects)
def changeConf(self, conf):
async def changeConf(self, conf):
yield self.board.getConf(conf)
await self.board.getConf(conf)
self.board.setUpdateTime()
result = "Board configuration changed!"
LOG.info("PROVISIONING RESULT: " + str(result))
returnValue(result)
return result
def destroyNode(self, conf):
async def destroyNode(self, conf):
yield self.board.setConf(conf)
await self.board.setConf(conf)
result = "Board configuration cleaned!"
LOG.info("DESTROY RESULT: " + str(result))
returnValue(result)
return result

View File

@ -13,6 +13,8 @@
# License for the specific language governing permissions and limitations
# under the License.
__author__ = "Nicola Peditto <npeditto@unime.it"
import errno
from fuse import FuseOSError
import os

View File

@ -12,15 +12,14 @@
# 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 with_statement
__author__ = "Nicola Peditto <npeditto@unime.it"
import errno
import os
from subprocess import call
import threading
from twisted.internet.defer import inlineCallbacks
from twisted.internet.defer import returnValue
# Iotronic imports
from iotronic_lightningrod.modules import Module
@ -71,7 +70,7 @@ class VfsManager(Module.Module):
result = "Mounting error:", msg
print(result)
yield returnValue(result)
return result
def unmountLocal(self, mountPoint):
@ -88,7 +87,7 @@ class VfsManager(Module.Module):
result = "Unmounting error:", msg
print(result)
yield returnValue(result)
return result
def mountRemote(self,
mountSource,
@ -116,7 +115,7 @@ class VfsManager(Module.Module):
result = "Mounting error:", msg
print(result)
yield returnValue(result)
return result
def unmountRemote(self, mountPoint):
@ -133,7 +132,7 @@ class VfsManager(Module.Module):
result = "Unmounting error:", msg
print(result)
yield returnValue(result)
return result
class MounterLocal(threading.Thread):
@ -209,12 +208,11 @@ class MounterRemote(threading.Thread):
LOG.error("Mounting FUSE error: " + str(msg))
@inlineCallbacks
def makeCall(msg=None, agent=None, session=None):
async def makeCall(msg=None, agent=None, session=None):
rpc_addr = str(agent) + '.stack4things.echo'
LOG.debug("VFS - I'm calling " + rpc_addr)
try:
res = yield session.call(rpc_addr, msg)
res = await session.call(rpc_addr, msg)
LOG.info("NOTIFICATION " + str(res))
except Exception as e:
LOG.warning("NOTIFICATION error: {0}".format(e))

View File

@ -13,6 +13,7 @@
# License for the specific language governing permissions and limitations
# under the License.
__author__ = "Nicola Peditto <npeditto@unime.it"
import abc
import six

View File

@ -13,14 +13,14 @@
# License for the specific language governing permissions and limitations
# under the License.
import cPickle as pickle
# import oslo_messaging
__author__ = "Nicola Peditto <npeditto@unime.it"
import _pickle as pickle
from oslo_log import log as logging
LOG = logging.getLogger(__name__)
# class ObjectSerializer(oslo_messaging.NoOpSerializer):
class ObjectSerializer(object):
"""A PluginObject-aware Serializer.
@ -31,7 +31,6 @@ class ObjectSerializer(object):
and RpcDispatcher objects.
"""
# def serialize_entity(self, context, entity):
def serialize_entity(self, entity):
dumped = pickle.dumps(entity, 0)
@ -40,10 +39,9 @@ class ObjectSerializer(object):
return dumped
# def deserialize_entity(self, context, entity):
def deserialize_entity(self, entity):
loaded = pickle.loads(str(entity))
loaded = pickle.loads(str.encode(entity))
# LOG.debug(" - plugin deserialized")

View File

@ -13,6 +13,8 @@
# License for the specific language governing permissions and limitations
# under the License.
__author__ = "Nicola Peditto <npeditto@unime.it"
import httplib2
import json

View File

@ -1,70 +1,70 @@
# Copyright 2017 MDSLAB - University of Messina
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# 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 iotronic_lightningrod.devices.gpio import yun
from iotronic_lightningrod.plugins import Plugin
from oslo_log import log as logging
LOG = logging.getLogger(__name__)
# User imports
import datetime
import math
import time
ADCres = 1023.0
Beta = 3950
Kelvin = 273.15
Rb = 10000
Ginf = 120.6685
# User global variables
resource_id = "" # temperature resource id
action_URL = "http://smartme-data.unime.it/api/3/action/datastore_upsert"
api_key = ''
headers = {
"Content-Type": "application/json",
'Authorization': "" + api_key + ""
}
polling_time = 10
class Worker(Plugin.Plugin):
def __init__(self, name, params=None):
super(Worker, self).__init__(name, params)
def run(self):
device = yun.YunGpio()
while (self._is_running):
voltage = device._readVoltage("A0")
Rthermistor = float(Rb) * (float(ADCres) / float(voltage) - 1)
rel_temp = float(Beta) / (math.log(
float(Rthermistor) * float(Ginf))
)
temp = rel_temp - Kelvin
m_value = str(temp)
m_timestamp = datetime.datetime.now().strftime(
'%Y-%m-%dT%H:%M:%S.%f'
)
LOG.info(m_value + " - " + m_timestamp)
time.sleep(polling_time)
# Copyright 2017 MDSLAB - University of Messina
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# 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 iotronic_lightningrod.devices.gpio import yun
from iotronic_lightningrod.plugins import Plugin
from oslo_log import log as logging
LOG = logging.getLogger(__name__)
# User imports
import datetime
import math
import time
ADCres = 1023.0
Beta = 3950
Kelvin = 273.15
Rb = 10000
Ginf = 120.6685
# User global variables
resource_id = "" # temperature resource id
action_URL = "http://smartme-data.unime.it/api/3/action/datastore_upsert"
api_key = ''
headers = {
"Content-Type": "application/json",
'Authorization': "" + api_key + ""
}
polling_time = 10
class Worker(Plugin.Plugin):
def __init__(self, name, params=None):
super(Worker, self).__init__(name, params)
def run(self):
device = yun.YunGpio()
while (self._is_running):
voltage = device._readVoltage("A0")
Rthermistor = float(Rb) * (float(ADCres) / float(voltage) - 1)
rel_temp = float(Beta) / (math.log(
float(Rthermistor) * float(Ginf))
)
temp = rel_temp - Kelvin
m_value = str(temp)
m_timestamp = datetime.datetime.now().strftime(
'%Y-%m-%dT%H:%M:%S.%f'
)
LOG.info(m_value + " - " + m_timestamp)
time.sleep(polling_time)

View File

@ -0,0 +1,3 @@
{
"pin":0
}

View File

@ -0,0 +1,37 @@
# Copyright 2017 MDSLAB - University of Messina
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# 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 iotronic_lightningrod.plugins import Plugin
from iotronic_lightningrod.plugins import pluginApis as API
from oslo_log import log as logging
LOG = logging.getLogger(__name__)
# User imports
device = API.getBoardGpio()
class Worker(Plugin.Plugin):
def __init__(self, uuid, name, q_result, params=None):
super(Worker, self).__init__(uuid, name, q_result, params)
def run(self):
LOG.info("Input parameters: " + str(self.params))
device._setGPIOs("D13", "out", str(self.params['pin']))
LOG.info("Plugin " + self.name + " process completed!")
self.q_result.put("Led D13: " + str(self.params['pin']))

View File

@ -14,6 +14,7 @@
# under the License.
from iotronic_lightningrod.plugins import Plugin
# from iotronic_lightningrod.plugins import pluginApis as API
from oslo_log import log as logging
LOG = logging.getLogger(__name__)

View File

@ -1,13 +0,0 @@
{
"polling" : "600",
"ckan_enabled" : false,
"temperature": { "pin" : "A0", "enabled":true },
"brightness": { "pin" : "A1", "enabled":true },
"humidity": { "pin" : "A2", "enabled":true },
"gas": { "pin" : "A3", "enabled":true },
"noise": { "pin" : "A4", "enabled":true },
"pressure": { "pin" : "i2c", "enabled":true }
}
{"delay" : 10}

View File

@ -1,409 +0,0 @@
# Copyright 2017 MDSLAB - University of Messina
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# 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 iotronic_lightningrod.plugins import Plugin
from iotronic_lightningrod.plugins import pluginApis as API
from oslo_log import log as logging
LOG = logging.getLogger(__name__)
# User imports
import datetime
import json
import math
import threading
import time
# User global variables
ckan_addr = 'smartme-data.unime.it'
action_URL = "http://" + ckan_addr + "/api/3/action/datastore_upsert"
api_key = '22c5cfa7-9dea-4dd9-9f9d-eedf296852ae'
headers = {
"Content-Type": "application/json",
'Authorization': "" + api_key + ""
}
sensors_list = [
'temperature',
'brightness',
'humidity',
'pressure',
'noise'
# , 'gas'
]
position = None
SENSORS = {}
location = {}
device = API.getBoardGpio()
THR_KILL = None
# Sensors gloabl parameters
# Temperature Parameters
ADCres = 1023.0
Beta = 3950
Kelvin = 273.15
Rb = 10000
Ginf = 120.6685
latest_temp = None
# Noise Parameters
samples_number = 1000
amplitudes_sum = 0
amplitudes_count = 0
def Temperature():
"""To get Temperature value.
:return: Temperature value (float)
"""
try:
voltage = device._readVoltage(SENSORS['temperature']['pin'])
Rthermistor = float(Rb) * (float(ADCres) / float(voltage) - 1)
rel_temp = float(Beta) / (math.log(float(Rthermistor) * float(Ginf)))
temp = rel_temp - Kelvin
# LOG.info("Temperature " + str(temp) + u" \u2103")
except Exception as err:
LOG.error("Error getting temperature: " + str(err))
return temp
def Brightness():
"""To get Brightness value.
:return: Brightness value (float)
"""
try:
voltage = float(device._readVoltage(SENSORS['brightness']['pin']))
ldr = (2500 / (5 - voltage * float(0.004887)) - 500) / float(3.3)
LOG.info("Brightness: " + str(ldr) + " (lux)")
except Exception as err:
LOG.error("Error getting brightness: " + str(err))
return ldr
def Humidity():
"""To get Humidity value: this function uses the Temperature sensor too.
:return: Humidity value (float)
"""
try:
degCelsius = Temperature()
supplyVolt = float(4.64)
HIH4030_Value = float(device._readVoltage(SENSORS['humidity']['pin']))
voltage = HIH4030_Value / float(1023.) * supplyVolt
sensorRH = float(161.0) * float(voltage) / supplyVolt - float(25.8)
relHum = sensorRH / (float(1.0546) - float(0.0026) * degCelsius)
LOG.info("Humidity " + str(relHum) + " percent")
except Exception as err:
LOG.error("Error getting humidity: " + str(err))
return relHum
def Pressure():
"""To get Pressure value.
:return: Pressure value (float)
"""
try:
in_pressure_raw = device.i2cRead('pressure')
pressure = float(in_pressure_raw) * float(0.00025) * 10
LOG.info("Pressure: " + str(pressure) + " hPa")
except Exception as err:
LOG.error("Error getting pressure: " + str(err))
return pressure
def Noise():
"""To get Noise value.
Elaborate a noise avarange value from noise listener.
:return: Noise value (float)
"""
try:
global amplitudes_sum, amplitudes_count
if amplitudes_count == float(0):
amplitude = float(0)
else:
amplitude = float(amplitudes_sum / amplitudes_count)
amplitudes_sum = 0
amplitudes_count = 0
except Exception as err:
LOG.error("Error getting noise: " + str(err))
return amplitude
def noise_listner():
"""Each two seconds collect a Noise sample.
"""
global THR_KILL
vect = []
if THR_KILL:
# LOG.info("listening noise..." + str(THR_KILL))
for x in range(samples_number):
read = float(device._readVoltage(SENSORS['noise']['pin']))
vect.append(read)
sorted_vect = sorted(vect)
minimum = float(sorted_vect[50])
maximum = float(sorted_vect[samples_number - 51])
tmp_amplitude = float(maximum - minimum)
global amplitudes_sum, amplitudes_count
amplitudes_sum = float(amplitudes_sum + tmp_amplitude)
amplitudes_count = float(amplitudes_count + 1)
# LOG.info("amplitudes_sum = " + str(amplitudes_sum))
# LOG.info("amplitudes_count = " + str(amplitudes_count))
threading.Timer(2.0, noise_listner).start()
else:
LOG.debug("Cancelled SmartME noise listening: " + str(THR_KILL))
def getMetric(metric, ckan):
"""Function to get metric values.
This function call the function relative to the 'metric'
specified and if the 'ckan' flag is True we create the body for the
REST request to send to CKAN database to store the sample there;
:param metric: name of the metric analized: 'Temperature', etc
:param ckan: flag True --> 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!")

View File

@ -13,6 +13,8 @@
# License for the specific language governing permissions and limitations
# under the License.
__author__ = "Nicola Peditto <npeditto@unime.it"
import json
SUCCESS = 'SUCCESS'

View File

@ -2,11 +2,11 @@
# of appearance. Changing the order has an impact on the overall integration
# process, which may cause wedges in the gate later.
pbr>=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
oslo.config>=5.1.0 # Apache-2.0
oslo.log>=3.36.0 # Apache-2.0

View File

@ -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

36
tox.ini
View File

@ -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
ignore = E711,E712,H404,H405,E123,E125,E901
exclude = .venv,.git,.tox,dist,doc,etc,*lib/python*,*egg,build,iotronic_lightningrod/plugins/plugins_examples/