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:
parent
f953021d76
commit
1fd573e3b2
|
@ -1,3 +1,4 @@
|
|||
[DEFAULT]
|
||||
debug = True
|
||||
log_file = /var/log/iotronic/lightning-rod.log
|
||||
lightningrod_home = /var/lib/iotronic
|
||||
|
|
|
@ -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()
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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__)
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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()
|
||||
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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!")
|
||||
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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")
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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)
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"pin":0
|
||||
}
|
|
@ -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']))
|
|
@ -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__)
|
|
@ -0,0 +1 @@
|
|||
{}
|
|
@ -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}
|
|
@ -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!")
|
|
@ -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'
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
36
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
|
||||
ignore = E711,E712,H404,H405,E123,E125,E901
|
||||
exclude = .venv,.git,.tox,dist,doc,etc,*lib/python*,*egg,build,iotronic_lightningrod/plugins/plugins_examples/
|
Loading…
Reference in New Issue