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]
|
[DEFAULT]
|
||||||
debug = True
|
debug = True
|
||||||
log_file = /var/log/iotronic/lightning-rod.log
|
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
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
|
__author__ = "Nicola Peditto <npeditto@unime.it"
|
||||||
|
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
# from dateutil.tz import tzlocal
|
|
||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
|
|
||||||
from iotronic_lightningrod.config import iotronic_home
|
from oslo_config import cfg
|
||||||
|
|
||||||
from oslo_log import log as logging
|
from oslo_log import log as logging
|
||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
SETTINGS = iotronic_home + '/settings.json'
|
CONF = cfg.CONF
|
||||||
|
|
||||||
|
SETTINGS = '/etc/iotronic/settings.json'
|
||||||
|
|
||||||
|
|
||||||
class Board(object):
|
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
|
# Load all settings.json file
|
||||||
self.iotronic_config = self.loadConf()
|
self.iotronic_config = self.loadConf()
|
||||||
|
|
||||||
|
|
|
@ -13,6 +13,8 @@
|
||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
|
__author__ = "Nicola Peditto <npeditto@unime.it"
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import signal
|
import signal
|
||||||
|
|
||||||
|
|
|
@ -13,6 +13,7 @@
|
||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
|
__author__ = "Nicola Peditto <npeditto@unime.it"
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import pkg_resources
|
import pkg_resources
|
||||||
|
@ -23,6 +24,3 @@ entry_points_name = \
|
||||||
|
|
||||||
# Iotronic python package folder
|
# Iotronic python package folder
|
||||||
package_path = os.path.join(dist.location, __package__)
|
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
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
__author__ = "MDSLAB Team"
|
__author__ = "Nicola Peditto <npeditto@unime.it"
|
||||||
|
|
||||||
import abc
|
import abc
|
||||||
import six
|
import six
|
||||||
|
|
|
@ -13,6 +13,7 @@
|
||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
|
__author__ = "Nicola Peditto <npeditto@unime.it"
|
||||||
|
|
||||||
import abc
|
import abc
|
||||||
import six
|
import six
|
||||||
|
|
|
@ -13,6 +13,7 @@
|
||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
|
__author__ = "Nicola Peditto <npeditto@unime.it"
|
||||||
|
|
||||||
from oslo_log import log as logging
|
from oslo_log import log as logging
|
||||||
|
|
||||||
|
|
|
@ -13,10 +13,13 @@
|
||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
from iotronic_lightningrod.devices.gpio import Gpio
|
__author__ = "Nicola Peditto <npeditto@unime.it"
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import time
|
import time
|
||||||
|
|
||||||
|
from iotronic_lightningrod.devices.gpio import Gpio
|
||||||
|
|
||||||
from oslo_log import log as logging
|
from oslo_log import log as logging
|
||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
|
@ -13,8 +13,9 @@
|
||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
|
__author__ = "Nicola Peditto <npeditto@unime.it"
|
||||||
|
|
||||||
import inspect
|
import inspect
|
||||||
from twisted.internet.defer import returnValue
|
|
||||||
|
|
||||||
from iotronic_lightningrod.devices import Device
|
from iotronic_lightningrod.devices import Device
|
||||||
from iotronic_lightningrod.devices.gpio import server
|
from iotronic_lightningrod.devices.gpio import server
|
||||||
|
@ -46,10 +47,10 @@ class System(Device.Device):
|
||||||
"""
|
"""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def testRPC(self):
|
async def testRPC(self):
|
||||||
rpc_name = whoami()
|
rpc_name = whoami()
|
||||||
LOG.info("RPC " + rpc_name + " CALLED...")
|
LOG.info("RPC " + rpc_name + " CALLED...")
|
||||||
yield makeNothing()
|
await makeNothing()
|
||||||
result = " - " + rpc_name + " result: testRPC is working!!!\n"
|
result = " - " + rpc_name + " result: testRPC is working!!!\n"
|
||||||
LOG.info(result)
|
LOG.info(result)
|
||||||
returnValue(result)
|
return result
|
||||||
|
|
|
@ -13,9 +13,7 @@
|
||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
# Linino references: http://wiki.linino.org/doku.php?id=wiki:lininoio_sysfs
|
__author__ = "Nicola Peditto <npeditto@unime.it"
|
||||||
|
|
||||||
from twisted.internet.defer import returnValue
|
|
||||||
|
|
||||||
from iotronic_lightningrod.devices import Device
|
from iotronic_lightningrod.devices import Device
|
||||||
from iotronic_lightningrod.devices.gpio import yun
|
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
|
from oslo_log import log as logging
|
||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
# Linino references: http://wiki.linino.org/doku.php?id=wiki:lininoio_sysfs
|
||||||
|
|
||||||
|
|
||||||
class System(Device.Device):
|
class System(Device.Device):
|
||||||
|
|
||||||
|
@ -41,25 +41,25 @@ class System(Device.Device):
|
||||||
"""
|
"""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def testLED(self):
|
async def testLED(self):
|
||||||
LOG.info(" - testLED CALLED...")
|
LOG.info(" - testLED CALLED...")
|
||||||
|
|
||||||
yield self.gpio.blinkLed()
|
await self.gpio.blinkLed()
|
||||||
|
|
||||||
result = "testLED: LED blinking!\n"
|
result = "testLED: LED blinking!\n"
|
||||||
LOG.info(result)
|
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
|
LOG.info(" - setGPIOs CALLED... digital pin " + Dpin
|
||||||
+ " (GPIO n. " + self.gpio.MAPPING[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)
|
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
|
"""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)
|
voltage = self.gpio._readVoltage(Apin)
|
||||||
|
|
||||||
result = yield "read voltage for " + Apin + " pin: " + voltage
|
result = await "read voltage for " + Apin + " pin: " + voltage
|
||||||
LOG.info(result)
|
LOG.info(result)
|
||||||
returnValue(result)
|
return result
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
# Copyright 2017 MDSLAB - University of Messina
|
# Copyright 2017 MDSLAB - University of Messina All Rights Reserved.
|
||||||
# All Rights Reserved.
|
|
||||||
#
|
#
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
# 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
|
# 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
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
|
__author__ = "Nicola Peditto <npeditto@unime.it"
|
||||||
|
|
||||||
# Autobahn and Twisted imports
|
# Autobahn imports
|
||||||
from autobahn.twisted import wamp
|
import asyncio
|
||||||
from autobahn.twisted.wamp import ApplicationSession
|
from autobahn.asyncio.component import Component
|
||||||
from autobahn.twisted import websocket
|
|
||||||
from autobahn.wamp import exception
|
from autobahn.wamp import exception
|
||||||
from autobahn.wamp import types
|
import txaio
|
||||||
from twisted.internet.defer import inlineCallbacks
|
|
||||||
from twisted.internet.protocol import ReconnectingClientFactory
|
|
||||||
from twisted.internet import reactor
|
|
||||||
|
|
||||||
# OSLO imports
|
# OSLO imports
|
||||||
from oslo_config import cfg
|
from oslo_config import cfg
|
||||||
|
@ -33,7 +29,6 @@ import inspect
|
||||||
import os
|
import os
|
||||||
import pkg_resources
|
import pkg_resources
|
||||||
import signal
|
import signal
|
||||||
import socket
|
|
||||||
from stevedore import extension
|
from stevedore import extension
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
|
@ -46,7 +41,17 @@ import iotronic_lightningrod.wampmessage as WM
|
||||||
|
|
||||||
# Global variables
|
# Global variables
|
||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
lr_opts = [
|
||||||
|
cfg.StrOpt('lightningrod_home',
|
||||||
|
default='/var/lib/iotronic',
|
||||||
|
help=('Lightning Home Data')),
|
||||||
|
]
|
||||||
|
|
||||||
CONF = cfg.CONF
|
CONF = cfg.CONF
|
||||||
|
CONF.register_opts(lr_opts)
|
||||||
|
|
||||||
SESSION = None
|
SESSION = None
|
||||||
global board
|
global board
|
||||||
board = None
|
board = None
|
||||||
|
@ -54,6 +59,13 @@ reconnection = False
|
||||||
RPC = {}
|
RPC = {}
|
||||||
RPC_devices = {}
|
RPC_devices = {}
|
||||||
|
|
||||||
|
# ASYNCIO
|
||||||
|
loop = None
|
||||||
|
component = None
|
||||||
|
txaio.start_logging(level="info")
|
||||||
|
RUNNER = None
|
||||||
|
connected = False
|
||||||
|
|
||||||
|
|
||||||
def moduleReloadInfo(session):
|
def moduleReloadInfo(session):
|
||||||
"""This function is used in the reconnection stage to register
|
"""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
|
# We don't considere the __init__ and finalize methods
|
||||||
if (meth[0] != "__init__") & (meth[0] != "finalize"):
|
if (meth[0] != "__init__") & (meth[0] != "finalize"):
|
||||||
rpc_addr = u'iotronic.' + board.uuid + '.' + meth[0]
|
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(meth[0]))
|
||||||
# LOG.info(" --> " + str(rpc_addr))
|
|
||||||
|
|
||||||
|
|
||||||
def modulesLoader(session):
|
def modulesLoader(session):
|
||||||
|
@ -169,8 +182,7 @@ def modulesLoader(session):
|
||||||
LOG.info("\n\nListening...")
|
LOG.info("\n\nListening...")
|
||||||
|
|
||||||
|
|
||||||
@inlineCallbacks
|
async def IotronicLogin(board, session, details):
|
||||||
def IotronicLogin(board, session, details):
|
|
||||||
"""Function called to connect the board to Iotronic.
|
"""Function called to connect the board to Iotronic.
|
||||||
|
|
||||||
The board:
|
The board:
|
||||||
|
@ -187,18 +199,16 @@ def IotronicLogin(board, session, details):
|
||||||
|
|
||||||
global reconnection
|
global reconnection
|
||||||
|
|
||||||
global SESSION
|
|
||||||
SESSION = session
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
|
||||||
rpc = str(board.agent) + u'.stack4things.connection'
|
rpc = str(board.agent) + u'.stack4things.connection'
|
||||||
|
|
||||||
with timeoutRPC(seconds=3, action=rpc):
|
with timeoutRPC(seconds=3, action=rpc):
|
||||||
res = yield session.call(rpc,
|
res = await session.call(
|
||||||
uuid=board.uuid,
|
rpc,
|
||||||
session=details.session
|
uuid=board.uuid,
|
||||||
)
|
session=details.session
|
||||||
|
)
|
||||||
|
|
||||||
w_msg = WM.deserialize(res)
|
w_msg = WM.deserialize(res)
|
||||||
|
|
||||||
|
@ -209,14 +219,14 @@ def IotronicLogin(board, session, details):
|
||||||
# LOADING BOARD MODULES
|
# LOADING BOARD MODULES
|
||||||
try:
|
try:
|
||||||
|
|
||||||
yield modulesLoader(session)
|
modulesLoader(session)
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
LOG.warning("WARNING - Could not register procedures: "
|
LOG.warning("WARNING - Could not register procedures: "
|
||||||
+ str(e))
|
+ str(e))
|
||||||
|
|
||||||
# Reset flag to False
|
# Reset flag to False
|
||||||
reconnection = False
|
# reconnection = False
|
||||||
|
|
||||||
else:
|
else:
|
||||||
LOG.error(" - Access denied to Iotronic.")
|
LOG.error(" - Access denied to Iotronic.")
|
||||||
|
@ -228,324 +238,16 @@ def IotronicLogin(board, session, details):
|
||||||
# the "stack4things.connection" RPC.
|
# the "stack4things.connection" RPC.
|
||||||
# The board will disconnect from WAMP agent and retry later.
|
# The board will disconnect from WAMP agent and retry later.
|
||||||
reconnection = True
|
reconnection = True
|
||||||
session.disconnect()
|
|
||||||
|
# We stop the Component in order to trigger the onDisconnect event
|
||||||
|
component.stop()
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
LOG.warning("Iotronic board connection error: " + str(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):
|
def wampConnect(wamp_conf):
|
||||||
"""WAMP connection procedure.
|
"""WAMP connection procedures.
|
||||||
|
|
||||||
:param wamp_conf: WAMP configuration from settings.json file
|
:param wamp_conf: WAMP configuration from settings.json file
|
||||||
|
|
||||||
|
@ -555,32 +257,326 @@ def wampConnect(wamp_conf):
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
|
||||||
component_config = types.ComponentConfig(
|
LOG.info("WAMP status:" +
|
||||||
realm=unicode(wamp_conf['realm'])
|
"\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(
|
# To manage the registration stage: we got the info for the main
|
||||||
session_factory,
|
# WAMP agent and LR is going to connect to it starting the Component
|
||||||
url=wamp_conf['url']
|
# with the new WAMP configuration.
|
||||||
)
|
if connected == False and board.status == "registered" \
|
||||||
transport_factory.autoPingInterval = 5
|
and reconnection == False:
|
||||||
transport_factory.autoPingTimeout = 5
|
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:
|
global connected
|
||||||
LOG.error(" - IP address validation error: " + str(err))
|
connected = True
|
||||||
Bye()
|
|
||||||
|
# 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:
|
except Exception as err:
|
||||||
LOG.error(" - URI validation error: " + str(err))
|
LOG.error(" - URI validation error: " + str(err))
|
||||||
|
@ -594,34 +590,21 @@ class WampManager(object):
|
||||||
|
|
||||||
def __init__(self, wamp_conf):
|
def __init__(self, wamp_conf):
|
||||||
|
|
||||||
# Connection to Crossbar server.
|
# wampConnect configures and manages the connection to Crossbar server.
|
||||||
wampConnect(wamp_conf)
|
wampConnect(wamp_conf)
|
||||||
|
|
||||||
def start(self):
|
def start(self):
|
||||||
LOG.info(" - starting Lightning-rod WAMP server...")
|
LOG.info(" - starting Lightning-rod WAMP server...")
|
||||||
reactor.run()
|
|
||||||
|
|
||||||
"""
|
global loop
|
||||||
# TEMPORARY ------------------------------------------------------
|
loop = asyncio.get_event_loop()
|
||||||
from subprocess import call
|
component.start(loop)
|
||||||
LOG.debug("Unmounting...")
|
loop.run_forever()
|
||||||
|
|
||||||
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)
|
|
||||||
# ------------------------------------------------------------------
|
|
||||||
"""
|
|
||||||
|
|
||||||
def stop(self):
|
def stop(self):
|
||||||
LOG.info("Stopping WAMP agent server...")
|
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!")
|
LOG.info("WAMP server stopped!")
|
||||||
|
|
||||||
|
|
||||||
|
@ -646,6 +629,9 @@ class LightningRod(object):
|
||||||
CONF(project='iotronic')
|
CONF(project='iotronic')
|
||||||
logging.setup(CONF, DOMAIN)
|
logging.setup(CONF, DOMAIN)
|
||||||
|
|
||||||
|
if CONF.debug:
|
||||||
|
txaio.start_logging(level="debug")
|
||||||
|
|
||||||
signal.signal(signal.SIGINT, self.stop_handler)
|
signal.signal(signal.SIGINT, self.stop_handler)
|
||||||
|
|
||||||
LogoLR()
|
LogoLR()
|
||||||
|
@ -664,9 +650,7 @@ class LightningRod(object):
|
||||||
|
|
||||||
def stop_handler(self, signum, frame):
|
def stop_handler(self, signum, frame):
|
||||||
LOG.info("LR is shutting down...")
|
LOG.info("LR is shutting down...")
|
||||||
|
|
||||||
self.w.stop()
|
self.w.stop()
|
||||||
|
|
||||||
Bye()
|
Bye()
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -13,7 +13,7 @@
|
||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
__author__ = "MDSLAB Team"
|
__author__ = "Nicola Peditto <npeditto@unime.it"
|
||||||
|
|
||||||
import abc
|
import abc
|
||||||
import six
|
import six
|
||||||
|
|
|
@ -13,10 +13,11 @@
|
||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
|
__author__ = "Nicola Peditto <npeditto@unime.it"
|
||||||
|
|
||||||
import imp
|
import imp
|
||||||
import inspect
|
import inspect
|
||||||
import os
|
import os
|
||||||
from twisted.internet.defer import inlineCallbacks
|
|
||||||
|
|
||||||
from iotronic_lightningrod.config import package_path
|
from iotronic_lightningrod.config import package_path
|
||||||
from iotronic_lightningrod.lightningrod import RPC_devices
|
from iotronic_lightningrod.lightningrod import RPC_devices
|
||||||
|
@ -39,7 +40,7 @@ def deviceWampRegister(dev_meth_list, board):
|
||||||
# LOG.info(" - " + str(meth[0]))
|
# LOG.info(" - " + str(meth[0]))
|
||||||
rpc_addr = u'iotronic.' + board.uuid + '.' + meth[0]
|
rpc_addr = u'iotronic.' + board.uuid + '.' + meth[0]
|
||||||
# LOG.debug(" --> " + str(rpc_addr))
|
# LOG.debug(" --> " + str(rpc_addr))
|
||||||
SESSION.register(inlineCallbacks(meth[1]), rpc_addr)
|
SESSION.register(meth[1], rpc_addr)
|
||||||
|
|
||||||
LOG.info(" --> " + str(meth[0]) + " registered!")
|
LOG.info(" --> " + str(meth[0]) + " registered!")
|
||||||
|
|
||||||
|
|
|
@ -12,30 +12,31 @@
|
||||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
from __future__ import absolute_import
|
from __future__ import absolute_import
|
||||||
|
|
||||||
|
__author__ = "Nicola Peditto <npeditto@unime.it"
|
||||||
|
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
import imp
|
import imp
|
||||||
import inspect
|
import inspect
|
||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
from Queue import Queue
|
import queue
|
||||||
import shutil
|
import shutil
|
||||||
import time
|
import time
|
||||||
from twisted.internet.defer import returnValue
|
|
||||||
|
|
||||||
from iotronic_lightningrod.config import iotronic_home
|
|
||||||
from iotronic_lightningrod.modules import Module
|
from iotronic_lightningrod.modules import Module
|
||||||
from iotronic_lightningrod.plugins import PluginSerializer
|
from iotronic_lightningrod.plugins import PluginSerializer
|
||||||
import iotronic_lightningrod.wampmessage as WM
|
import iotronic_lightningrod.wampmessage as WM
|
||||||
|
|
||||||
|
from oslo_config import cfg
|
||||||
from oslo_log import log as logging
|
from oslo_log import log as logging
|
||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
CONF = cfg.CONF
|
||||||
PLUGINS_THRS = {}
|
PLUGINS_THRS = {}
|
||||||
PLUGINS_CONF_FILE = iotronic_home + "/plugins.json"
|
PLUGINS_CONF_FILE = CONF.lightningrod_home + "/plugins.json"
|
||||||
|
|
||||||
|
|
||||||
def getFuncName():
|
def getFuncName():
|
||||||
|
@ -144,7 +145,8 @@ def RebootOnBootPlugins():
|
||||||
|
|
||||||
LOG.info(" - Rebooting plugin " + plugin_uuid)
|
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_filename = plugin_home + "/" + plugin_uuid + ".py"
|
||||||
plugin_params_file = \
|
plugin_params_file = \
|
||||||
plugin_home + "/" + plugin_uuid + ".json"
|
plugin_home + "/" + plugin_uuid + ".json"
|
||||||
|
@ -224,7 +226,7 @@ class PluginManager(Module.Module):
|
||||||
# Reboot boot enabled plugins
|
# Reboot boot enabled plugins
|
||||||
RebootOnBootPlugins()
|
RebootOnBootPlugins()
|
||||||
|
|
||||||
def PluginInject(self, plugin, onboot):
|
async def PluginInject(self, plugin, onboot):
|
||||||
"""Plugin injection procedure into the board:
|
"""Plugin injection procedure into the board:
|
||||||
|
|
||||||
1. get Plugin files
|
1. get Plugin files
|
||||||
|
@ -254,7 +256,8 @@ class PluginManager(Module.Module):
|
||||||
loaded = ser.deserialize_entity(code)
|
loaded = ser.deserialize_entity(code)
|
||||||
# LOG.debug("- plugin loaded code:\n" + loaded)
|
# 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_filename = plugin_path + plugin_uuid + ".py"
|
||||||
|
|
||||||
# Plugin folder creation if does not exist
|
# Plugin folder creation if does not exist
|
||||||
|
@ -311,17 +314,18 @@ class PluginManager(Module.Module):
|
||||||
json.dump(plugins_conf, f, indent=4)
|
json.dump(plugins_conf, f, indent=4)
|
||||||
|
|
||||||
LOG.info(" - " + message)
|
LOG.info(" - " + message)
|
||||||
w_msg = yield WM.WampSuccess(message)
|
w_msg = await WM.WampSuccess(message)
|
||||||
|
|
||||||
except Exception as err:
|
except Exception as err:
|
||||||
message = "Plugin injection error: " + str(err)
|
message = "Plugin injection error: " + str(err)
|
||||||
LOG.error(" - " + message)
|
LOG.error(" - " + message)
|
||||||
w_msg = yield WM.WampError(str(err))
|
w_msg = await WM.WampError(str(err))
|
||||||
returnValue(w_msg.serialize())
|
|
||||||
|
|
||||||
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;
|
"""To start an asynchronous plugin;
|
||||||
|
|
||||||
the plugin will run until the PluginStop is called.
|
the plugin will run until the PluginStop is called.
|
||||||
|
@ -352,12 +356,12 @@ class PluginManager(Module.Module):
|
||||||
message = "ALREADY STARTED!"
|
message = "ALREADY STARTED!"
|
||||||
LOG.warning(" - Plugin "
|
LOG.warning(" - Plugin "
|
||||||
+ plugin_uuid + " already started!")
|
+ plugin_uuid + " already started!")
|
||||||
w_msg = yield WM.WampError(message)
|
w_msg = await WM.WampError(message)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
|
|
||||||
plugin_home = \
|
plugin_home = \
|
||||||
iotronic_home + "/plugins/" + plugin_uuid
|
CONF.lightningrod_home + "/plugins/" + plugin_uuid
|
||||||
plugin_filename = \
|
plugin_filename = \
|
||||||
plugin_home + "/" + plugin_uuid + ".py"
|
plugin_home + "/" + plugin_uuid + ".py"
|
||||||
plugin_params_file = \
|
plugin_params_file = \
|
||||||
|
@ -404,32 +408,33 @@ class PluginManager(Module.Module):
|
||||||
|
|
||||||
response = "STARTED"
|
response = "STARTED"
|
||||||
LOG.info(" - " + worker.complete(rpc_name, response))
|
LOG.info(" - " + worker.complete(rpc_name, response))
|
||||||
w_msg = yield WM.WampSuccess(response)
|
w_msg = await WM.WampSuccess(response)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
message = \
|
message = \
|
||||||
rpc_name + " - ERROR " \
|
rpc_name + " - ERROR " \
|
||||||
+ plugin_filename + " does not exist!"
|
+ plugin_filename + " does not exist!"
|
||||||
LOG.error(" - " + message)
|
LOG.error(" - " + message)
|
||||||
w_msg = yield WM.WampError(message)
|
w_msg = await WM.WampError(message)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
message = "Plugin " + plugin_uuid \
|
message = "Plugin " + plugin_uuid \
|
||||||
+ " does not exist in this board!"
|
+ " does not exist in this board!"
|
||||||
LOG.warning(" - " + message)
|
LOG.warning(" - " + message)
|
||||||
w_msg = yield WM.WampError(message)
|
w_msg = await WM.WampError(message)
|
||||||
|
|
||||||
except Exception as err:
|
except Exception as err:
|
||||||
message = \
|
message = \
|
||||||
rpc_name + " - ERROR - plugin (" + plugin_uuid + ") - " \
|
rpc_name + " - ERROR - plugin (" + plugin_uuid + ") - " \
|
||||||
+ str(err)
|
+ str(err)
|
||||||
LOG.error(" - " + message)
|
LOG.error(" - " + message)
|
||||||
w_msg = yield WM.WampError(str(err))
|
w_msg = await WM.WampError(str(err))
|
||||||
returnValue(w_msg.serialize())
|
|
||||||
|
|
||||||
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
|
"""To stop an asynchronous plugin
|
||||||
|
|
||||||
:param plugin_uuid: ID of plufin to stop
|
:param plugin_uuid: ID of plufin to stop
|
||||||
|
@ -459,13 +464,13 @@ class PluginManager(Module.Module):
|
||||||
if 'delay' in parameters:
|
if 'delay' in parameters:
|
||||||
time.sleep(delay)
|
time.sleep(delay)
|
||||||
|
|
||||||
yield worker.stop()
|
await worker.stop()
|
||||||
|
|
||||||
del PLUGINS_THRS[plugin_uuid]
|
del PLUGINS_THRS[plugin_uuid]
|
||||||
|
|
||||||
message = "STOPPED"
|
message = "STOPPED"
|
||||||
LOG.info(" - " + worker.complete(rpc_name, message))
|
LOG.info(" - " + worker.complete(rpc_name, message))
|
||||||
w_msg = yield WM.WampSuccess(message)
|
w_msg = await WM.WampSuccess(message)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
message = \
|
message = \
|
||||||
|
@ -473,26 +478,27 @@ class PluginManager(Module.Module):
|
||||||
+ " - ERROR - plugin (" + plugin_uuid \
|
+ " - ERROR - plugin (" + plugin_uuid \
|
||||||
+ ") is instantiated but is not running anymore!"
|
+ ") is instantiated but is not running anymore!"
|
||||||
LOG.error(" - " + message)
|
LOG.error(" - " + message)
|
||||||
w_msg = yield WM.WampError(message)
|
w_msg = await WM.WampError(message)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
message = \
|
message = \
|
||||||
rpc_name + " - WARNING " \
|
rpc_name + " - WARNING " \
|
||||||
+ plugin_uuid + " is not running!"
|
+ plugin_uuid + " is not running!"
|
||||||
LOG.warning(" - " + message)
|
LOG.warning(" - " + message)
|
||||||
w_msg = yield WM.WampWarning(message)
|
w_msg = await WM.WampWarning(message)
|
||||||
|
|
||||||
except Exception as err:
|
except Exception as err:
|
||||||
message = \
|
message = \
|
||||||
rpc_name \
|
rpc_name \
|
||||||
+ " - ERROR - plugin (" + plugin_uuid + ") - " + str(err)
|
+ " - ERROR - plugin (" + plugin_uuid + ") - " + str(err)
|
||||||
LOG.error(" - " + message)
|
LOG.error(" - " + message)
|
||||||
w_msg = yield WM.WampError(str(err))
|
w_msg = await WM.WampError(str(err))
|
||||||
returnValue(w_msg.serialize())
|
|
||||||
|
|
||||||
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
|
"""To execute a synchronous plugin into the board
|
||||||
|
|
||||||
:param plugin_uuid:
|
:param plugin_uuid:
|
||||||
|
@ -512,11 +518,12 @@ class PluginManager(Module.Module):
|
||||||
|
|
||||||
message = "Plugin " + plugin_uuid + " already started!"
|
message = "Plugin " + plugin_uuid + " already started!"
|
||||||
LOG.warning(" - " + message)
|
LOG.warning(" - " + message)
|
||||||
w_msg = yield WM.WampWarning(message)
|
w_msg = WM.WampWarning(message)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
|
|
||||||
plugin_home = iotronic_home + "/plugins/" + plugin_uuid
|
plugin_home = CONF.lightningrod_home \
|
||||||
|
+ "/plugins/" + plugin_uuid
|
||||||
plugin_filename = plugin_home + "/" + plugin_uuid + ".py"
|
plugin_filename = plugin_home + "/" + plugin_uuid + ".py"
|
||||||
plugin_params_file = plugin_home + "/" + plugin_uuid + ".json"
|
plugin_params_file = plugin_home + "/" + plugin_uuid + ".json"
|
||||||
|
|
||||||
|
@ -532,14 +539,15 @@ class PluginManager(Module.Module):
|
||||||
|
|
||||||
LOG.info(" - Plugin " + plugin_uuid + " imported!")
|
LOG.info(" - Plugin " + plugin_uuid + " imported!")
|
||||||
|
|
||||||
q_result = Queue()
|
q_result = queue.Queue()
|
||||||
|
|
||||||
except Exception as err:
|
except Exception as err:
|
||||||
message = "Error importing plugin " \
|
message = "Error importing plugin " \
|
||||||
+ plugin_filename + ": " + str(err)
|
+ plugin_filename + ": " + str(err)
|
||||||
LOG.error(" - " + message)
|
LOG.error(" - " + message)
|
||||||
w_msg = yield WM.WampError(str(err))
|
w_msg = WM.WampError(str(err))
|
||||||
returnValue(w_msg.serialize())
|
|
||||||
|
return w_msg.serialize()
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
|
||||||
|
@ -575,33 +583,35 @@ class PluginManager(Module.Module):
|
||||||
response = q_result.get()
|
response = q_result.get()
|
||||||
|
|
||||||
LOG.info(" - " + worker.complete(rpc_name, response))
|
LOG.info(" - " + worker.complete(rpc_name, response))
|
||||||
w_msg = yield WM.WampSuccess(response)
|
w_msg = WM.WampSuccess(response)
|
||||||
|
|
||||||
except Exception as err:
|
except Exception as err:
|
||||||
message = "Error spawning plugin " \
|
message = "Error spawning plugin " \
|
||||||
+ plugin_filename + ": " + str(err)
|
+ plugin_filename + ": " + str(err)
|
||||||
LOG.error(" - " + message)
|
LOG.error(" - " + message)
|
||||||
w_msg = yield WM.WampError(str(err))
|
w_msg = WM.WampError(str(err))
|
||||||
returnValue(w_msg.serialize())
|
|
||||||
|
return w_msg.serialize()
|
||||||
|
|
||||||
else:
|
else:
|
||||||
message = \
|
message = \
|
||||||
rpc_name \
|
rpc_name \
|
||||||
+ " - ERROR " + plugin_filename + " does not exist!"
|
+ " - ERROR " + plugin_filename + " does not exist!"
|
||||||
LOG.error(" - " + message)
|
LOG.error(" - " + message)
|
||||||
w_msg = yield WM.WampError(message)
|
w_msg = WM.WampError(message)
|
||||||
|
|
||||||
except Exception as err:
|
except Exception as err:
|
||||||
message = \
|
message = \
|
||||||
rpc_name \
|
rpc_name \
|
||||||
+ " - ERROR - plugin (" + plugin_uuid + ") - " + str(err)
|
+ " - ERROR - plugin (" + plugin_uuid + ") - " + str(err)
|
||||||
LOG.error(" - " + message)
|
LOG.error(" - " + message)
|
||||||
w_msg = yield WM.WampError(str(err))
|
w_msg = WM.WampError(str(err))
|
||||||
returnValue(w_msg.serialize())
|
|
||||||
|
|
||||||
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
|
"""To remove a plugin from the board
|
||||||
|
|
||||||
:param plugin_uuid:
|
:param plugin_uuid:
|
||||||
|
@ -613,15 +623,16 @@ class PluginManager(Module.Module):
|
||||||
|
|
||||||
LOG.info("RPC " + rpc_name + " for plugin " + plugin_uuid)
|
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 \
|
if os.path.exists(plugin_path) is False \
|
||||||
or os.path.exists(PLUGINS_CONF_FILE) is False:
|
or os.path.exists(PLUGINS_CONF_FILE) is False:
|
||||||
|
|
||||||
message = "Plugin paths or files do not exist!"
|
message = "Plugin paths or files do not exist!"
|
||||||
LOG.error(message)
|
LOG.error(message)
|
||||||
w_msg = yield WM.WampError(message)
|
w_msg = await WM.WampError(message)
|
||||||
returnValue(w_msg.serialize())
|
|
||||||
|
return w_msg.serialize()
|
||||||
|
|
||||||
else:
|
else:
|
||||||
|
|
||||||
|
@ -641,8 +652,9 @@ class PluginManager(Module.Module):
|
||||||
message = "Removing plugin's files error in " \
|
message = "Removing plugin's files error in " \
|
||||||
+ plugin_path + ": " + str(err)
|
+ plugin_path + ": " + str(err)
|
||||||
LOG.error(" - " + message)
|
LOG.error(" - " + message)
|
||||||
w_msg = yield WM.WampError(str(err))
|
w_msg = await WM.WampError(str(err))
|
||||||
returnValue(w_msg.serialize())
|
|
||||||
|
return w_msg.serialize()
|
||||||
|
|
||||||
# Remove from plugins.json file its configuration
|
# Remove from plugins.json file its configuration
|
||||||
try:
|
try:
|
||||||
|
@ -678,22 +690,25 @@ class PluginManager(Module.Module):
|
||||||
+ plugin_uuid + " already removed!"
|
+ plugin_uuid + " already removed!"
|
||||||
LOG.warning(" - " + message)
|
LOG.warning(" - " + message)
|
||||||
|
|
||||||
w_msg = yield WM.WampSuccess(message)
|
w_msg = await WM.WampSuccess(message)
|
||||||
returnValue(w_msg.serialize())
|
|
||||||
|
return w_msg.serialize()
|
||||||
|
|
||||||
except Exception as err:
|
except Exception as err:
|
||||||
message = "Updating plugins.json error: " + str(err)
|
message = "Updating plugins.json error: " + str(err)
|
||||||
LOG.error(" - " + message)
|
LOG.error(" - " + message)
|
||||||
w_msg = yield WM.WampError(str(err))
|
w_msg = await WM.WampError(str(err))
|
||||||
returnValue(w_msg.serialize())
|
|
||||||
|
return w_msg.serialize()
|
||||||
|
|
||||||
except Exception as err:
|
except Exception as err:
|
||||||
message = "Plugin removing error: {0}".format(err)
|
message = "Plugin removing error: {0}".format(err)
|
||||||
LOG.error(" - " + message)
|
LOG.error(" - " + message)
|
||||||
w_msg = yield WM.WampError(str(err))
|
w_msg = await WM.WampError(str(err))
|
||||||
returnValue(w_msg.serialize())
|
|
||||||
|
|
||||||
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.
|
"""To reboot an asynchronous plugin (callable = false) into the board.
|
||||||
|
|
||||||
:return: return a response to RPC request
|
:return: return a response to RPC request
|
||||||
|
@ -710,7 +725,7 @@ class PluginManager(Module.Module):
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
|
||||||
plugin_home = iotronic_home + "/plugins/" + plugin_uuid
|
plugin_home = CONF.lightningrod_home + "/plugins/" + plugin_uuid
|
||||||
plugin_filename = plugin_home + "/" + plugin_uuid + ".py"
|
plugin_filename = plugin_home + "/" + plugin_uuid + ".py"
|
||||||
plugin_params_file = plugin_home + "/" + plugin_uuid + ".json"
|
plugin_params_file = plugin_home + "/" + plugin_uuid + ".json"
|
||||||
|
|
||||||
|
@ -774,23 +789,24 @@ class PluginManager(Module.Module):
|
||||||
|
|
||||||
message = "REBOOTED"
|
message = "REBOOTED"
|
||||||
LOG.info(" - " + worker.complete(rpc_name, message))
|
LOG.info(" - " + worker.complete(rpc_name, message))
|
||||||
w_msg = yield WM.WampSuccess(message)
|
w_msg = await WM.WampSuccess(message)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
message = "ERROR '" + plugin_filename + "' does not exist!"
|
message = "ERROR '" + plugin_filename + "' does not exist!"
|
||||||
LOG.error(" - " + message)
|
LOG.error(" - " + message)
|
||||||
w_msg = yield WM.WampError(message)
|
w_msg = await WM.WampError(message)
|
||||||
|
|
||||||
except Exception as err:
|
except Exception as err:
|
||||||
message = "Error rebooting plugin '" \
|
message = "Error rebooting plugin '" \
|
||||||
+ plugin_uuid + "': " + str(err)
|
+ plugin_uuid + "': " + str(err)
|
||||||
LOG.error(" - " + message)
|
LOG.error(" - " + message)
|
||||||
w_msg = yield WM.WampError(str(err))
|
w_msg = await WM.WampError(str(err))
|
||||||
returnValue(w_msg.serialize())
|
|
||||||
|
|
||||||
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
|
"""Check status thread plugin
|
||||||
|
|
||||||
:param plugin_uuid:
|
:param plugin_uuid:
|
||||||
|
@ -814,20 +830,21 @@ class PluginManager(Module.Module):
|
||||||
result = "DEAD"
|
result = "DEAD"
|
||||||
|
|
||||||
LOG.info(" - " + worker.complete(rpc_name, result))
|
LOG.info(" - " + worker.complete(rpc_name, result))
|
||||||
w_msg = yield WM.WampSuccess(result)
|
w_msg = await WM.WampSuccess(result)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
result = "DEAD"
|
result = "DEAD"
|
||||||
LOG.info(" - " + rpc_name + " result for "
|
LOG.info(" - " + rpc_name + " result for "
|
||||||
+ plugin_uuid + ": " + result)
|
+ plugin_uuid + ": " + result)
|
||||||
w_msg = yield WM.WampSuccess(result)
|
w_msg = await WM.WampSuccess(result)
|
||||||
|
|
||||||
except Exception as err:
|
except Exception as err:
|
||||||
message = \
|
message = \
|
||||||
rpc_name \
|
rpc_name \
|
||||||
+ " - ERROR - plugin (" + plugin_uuid + ") - " + str(err)
|
+ " - ERROR - plugin (" + plugin_uuid + ") - " + str(err)
|
||||||
LOG.error(" - " + message)
|
LOG.error(" - " + message)
|
||||||
w_msg = yield WM.WampError(str(err))
|
w_msg = await WM.WampError(str(err))
|
||||||
returnValue(w_msg.serialize())
|
|
||||||
|
|
||||||
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
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
|
__author__ = "Nicola Peditto <npeditto@unime.it"
|
||||||
|
|
||||||
|
import asyncio
|
||||||
|
|
||||||
from autobahn.twisted.util import sleep
|
|
||||||
from iotronic_lightningrod.modules import Module
|
from iotronic_lightningrod.modules import Module
|
||||||
from twisted.internet.defer import returnValue
|
|
||||||
|
|
||||||
from oslo_log import log as logging
|
from oslo_log import log as logging
|
||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
|
@ -28,15 +29,15 @@ class Test(Module.Module):
|
||||||
|
|
||||||
super(Test, self).__init__("Test", board)
|
super(Test, self).__init__("Test", board)
|
||||||
|
|
||||||
def test_function(self):
|
async def test_function(self):
|
||||||
import random
|
import random
|
||||||
s = random.uniform(0.5, 1.5)
|
s = random.uniform(0.5, 1.5)
|
||||||
yield sleep(s)
|
await asyncio.sleep(s)
|
||||||
result = "DEVICE test result: TEST!"
|
result = "DEVICE test result: TEST!"
|
||||||
LOG.info(result)
|
LOG.info(result)
|
||||||
returnValue(result)
|
return result
|
||||||
|
|
||||||
def add(self, x, y):
|
async def add(self, x, y):
|
||||||
c = yield x + y
|
c = await x + y
|
||||||
LOG.info("DEVICE add result: " + str(c))
|
LOG.info("DEVICE add result: " + str(c))
|
||||||
returnValue(c)
|
return c
|
||||||
|
|
|
@ -13,22 +13,21 @@
|
||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
|
__author__ = "Nicola Peditto <npeditto@unime.it"
|
||||||
|
|
||||||
from autobahn.twisted.util import sleep
|
import asyncio
|
||||||
from iotronic_lightningrod.config import entry_points_name
|
|
||||||
from iotronic_lightningrod.modules import Module
|
|
||||||
import pkg_resources
|
import pkg_resources
|
||||||
from six import moves
|
from six import moves
|
||||||
from stevedore import extension
|
from stevedore import extension
|
||||||
import sys
|
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
|
from oslo_log import log as logging
|
||||||
|
|
||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
from iotronic_lightningrod.lightningrod import SESSION
|
|
||||||
|
|
||||||
|
|
||||||
def refresh_stevedore(namespace=None):
|
def refresh_stevedore(namespace=None):
|
||||||
"""Trigger reload of entry points.
|
"""Trigger reload of entry points.
|
||||||
|
@ -62,17 +61,17 @@ class Utility(Module.Module):
|
||||||
def finalize(self):
|
def finalize(self):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def hello(self, client_name, message):
|
async def hello(self, client_name, message):
|
||||||
import random
|
import random
|
||||||
s = random.uniform(0.5, 3.0)
|
s = random.uniform(0.5, 3.0)
|
||||||
yield sleep(s)
|
await asyncio.sleep(s)
|
||||||
result = "Hello by board to Conductor " + client_name + \
|
result = "Hello by board to Conductor " + client_name + \
|
||||||
" that said me " + message + " - Time: " + '%.2f' % s
|
" that said me " + message + " - Time: " + '%.2f' % s
|
||||||
LOG.info("DEVICE hello result: " + str(result))
|
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)
|
LOG.info("LR modules loaded:\n\t" + new_module)
|
||||||
|
|
||||||
# Updating entry_points
|
# Updating entry_points
|
||||||
|
@ -92,28 +91,28 @@ class Utility(Module.Module):
|
||||||
for ep in pkg_resources.iter_entry_points(group='s4t.modules'):
|
for ep in pkg_resources.iter_entry_points(group='s4t.modules'):
|
||||||
named_objects.update({ep.name: ep.load()})
|
named_objects.update({ep.name: ep.load()})
|
||||||
|
|
||||||
yield named_objects
|
await named_objects
|
||||||
|
|
||||||
SESSION.disconnect()
|
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()
|
self.board.setUpdateTime()
|
||||||
|
|
||||||
result = "Board configuration changed!"
|
result = "Board configuration changed!"
|
||||||
LOG.info("PROVISIONING RESULT: " + str(result))
|
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!"
|
result = "Board configuration cleaned!"
|
||||||
LOG.info("DESTROY RESULT: " + str(result))
|
LOG.info("DESTROY RESULT: " + str(result))
|
||||||
|
|
||||||
returnValue(result)
|
return result
|
||||||
|
|
|
@ -13,6 +13,8 @@
|
||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
|
__author__ = "Nicola Peditto <npeditto@unime.it"
|
||||||
|
|
||||||
import errno
|
import errno
|
||||||
from fuse import FuseOSError
|
from fuse import FuseOSError
|
||||||
import os
|
import os
|
||||||
|
|
|
@ -12,15 +12,14 @@
|
||||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
from __future__ import with_statement
|
from __future__ import with_statement
|
||||||
|
|
||||||
|
__author__ = "Nicola Peditto <npeditto@unime.it"
|
||||||
|
|
||||||
import errno
|
import errno
|
||||||
import os
|
import os
|
||||||
from subprocess import call
|
from subprocess import call
|
||||||
import threading
|
import threading
|
||||||
from twisted.internet.defer import inlineCallbacks
|
|
||||||
from twisted.internet.defer import returnValue
|
|
||||||
|
|
||||||
# Iotronic imports
|
# Iotronic imports
|
||||||
from iotronic_lightningrod.modules import Module
|
from iotronic_lightningrod.modules import Module
|
||||||
|
@ -71,7 +70,7 @@ class VfsManager(Module.Module):
|
||||||
result = "Mounting error:", msg
|
result = "Mounting error:", msg
|
||||||
|
|
||||||
print(result)
|
print(result)
|
||||||
yield returnValue(result)
|
return result
|
||||||
|
|
||||||
def unmountLocal(self, mountPoint):
|
def unmountLocal(self, mountPoint):
|
||||||
|
|
||||||
|
@ -88,7 +87,7 @@ class VfsManager(Module.Module):
|
||||||
result = "Unmounting error:", msg
|
result = "Unmounting error:", msg
|
||||||
|
|
||||||
print(result)
|
print(result)
|
||||||
yield returnValue(result)
|
return result
|
||||||
|
|
||||||
def mountRemote(self,
|
def mountRemote(self,
|
||||||
mountSource,
|
mountSource,
|
||||||
|
@ -116,7 +115,7 @@ class VfsManager(Module.Module):
|
||||||
result = "Mounting error:", msg
|
result = "Mounting error:", msg
|
||||||
|
|
||||||
print(result)
|
print(result)
|
||||||
yield returnValue(result)
|
return result
|
||||||
|
|
||||||
def unmountRemote(self, mountPoint):
|
def unmountRemote(self, mountPoint):
|
||||||
|
|
||||||
|
@ -133,7 +132,7 @@ class VfsManager(Module.Module):
|
||||||
result = "Unmounting error:", msg
|
result = "Unmounting error:", msg
|
||||||
|
|
||||||
print(result)
|
print(result)
|
||||||
yield returnValue(result)
|
return result
|
||||||
|
|
||||||
|
|
||||||
class MounterLocal(threading.Thread):
|
class MounterLocal(threading.Thread):
|
||||||
|
@ -209,12 +208,11 @@ class MounterRemote(threading.Thread):
|
||||||
LOG.error("Mounting FUSE error: " + str(msg))
|
LOG.error("Mounting FUSE error: " + str(msg))
|
||||||
|
|
||||||
|
|
||||||
@inlineCallbacks
|
async def makeCall(msg=None, agent=None, session=None):
|
||||||
def makeCall(msg=None, agent=None, session=None):
|
|
||||||
rpc_addr = str(agent) + '.stack4things.echo'
|
rpc_addr = str(agent) + '.stack4things.echo'
|
||||||
LOG.debug("VFS - I'm calling " + rpc_addr)
|
LOG.debug("VFS - I'm calling " + rpc_addr)
|
||||||
try:
|
try:
|
||||||
res = yield session.call(rpc_addr, msg)
|
res = await session.call(rpc_addr, msg)
|
||||||
LOG.info("NOTIFICATION " + str(res))
|
LOG.info("NOTIFICATION " + str(res))
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
LOG.warning("NOTIFICATION error: {0}".format(e))
|
LOG.warning("NOTIFICATION error: {0}".format(e))
|
||||||
|
|
|
@ -13,6 +13,7 @@
|
||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
|
__author__ = "Nicola Peditto <npeditto@unime.it"
|
||||||
|
|
||||||
import abc
|
import abc
|
||||||
import six
|
import six
|
||||||
|
|
|
@ -13,14 +13,14 @@
|
||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
import cPickle as pickle
|
__author__ = "Nicola Peditto <npeditto@unime.it"
|
||||||
# import oslo_messaging
|
|
||||||
|
import _pickle as pickle
|
||||||
|
|
||||||
from oslo_log import log as logging
|
from oslo_log import log as logging
|
||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
# class ObjectSerializer(oslo_messaging.NoOpSerializer):
|
|
||||||
class ObjectSerializer(object):
|
class ObjectSerializer(object):
|
||||||
"""A PluginObject-aware Serializer.
|
"""A PluginObject-aware Serializer.
|
||||||
|
|
||||||
|
@ -31,7 +31,6 @@ class ObjectSerializer(object):
|
||||||
and RpcDispatcher objects.
|
and RpcDispatcher objects.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# def serialize_entity(self, context, entity):
|
|
||||||
def serialize_entity(self, entity):
|
def serialize_entity(self, entity):
|
||||||
|
|
||||||
dumped = pickle.dumps(entity, 0)
|
dumped = pickle.dumps(entity, 0)
|
||||||
|
@ -40,10 +39,9 @@ class ObjectSerializer(object):
|
||||||
|
|
||||||
return dumped
|
return dumped
|
||||||
|
|
||||||
# def deserialize_entity(self, context, entity):
|
|
||||||
def deserialize_entity(self, entity):
|
def deserialize_entity(self, entity):
|
||||||
|
|
||||||
loaded = pickle.loads(str(entity))
|
loaded = pickle.loads(str.encode(entity))
|
||||||
|
|
||||||
# LOG.debug(" - plugin deserialized")
|
# LOG.debug(" - plugin deserialized")
|
||||||
|
|
||||||
|
|
|
@ -13,6 +13,8 @@
|
||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
|
__author__ = "Nicola Peditto <npeditto@unime.it"
|
||||||
|
|
||||||
import httplib2
|
import httplib2
|
||||||
import json
|
import json
|
||||||
|
|
||||||
|
|
|
@ -1,70 +1,70 @@
|
||||||
# Copyright 2017 MDSLAB - University of Messina
|
# Copyright 2017 MDSLAB - University of Messina
|
||||||
# All Rights Reserved.
|
# All Rights Reserved.
|
||||||
#
|
#
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
# 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
|
# not use this file except in compliance with the License. You may obtain
|
||||||
# a copy of the License at
|
# a copy of the License at
|
||||||
#
|
#
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
#
|
#
|
||||||
# Unless required by applicable law or agreed to in writing, software
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
from iotronic_lightningrod.devices.gpio import yun
|
from iotronic_lightningrod.devices.gpio import yun
|
||||||
from iotronic_lightningrod.plugins import Plugin
|
from iotronic_lightningrod.plugins import Plugin
|
||||||
|
|
||||||
from oslo_log import log as logging
|
from oslo_log import log as logging
|
||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
# User imports
|
# User imports
|
||||||
import datetime
|
import datetime
|
||||||
import math
|
import math
|
||||||
import time
|
import time
|
||||||
|
|
||||||
ADCres = 1023.0
|
ADCres = 1023.0
|
||||||
Beta = 3950
|
Beta = 3950
|
||||||
Kelvin = 273.15
|
Kelvin = 273.15
|
||||||
Rb = 10000
|
Rb = 10000
|
||||||
Ginf = 120.6685
|
Ginf = 120.6685
|
||||||
|
|
||||||
# User global variables
|
# User global variables
|
||||||
resource_id = "" # temperature resource id
|
resource_id = "" # temperature resource id
|
||||||
action_URL = "http://smartme-data.unime.it/api/3/action/datastore_upsert"
|
action_URL = "http://smartme-data.unime.it/api/3/action/datastore_upsert"
|
||||||
api_key = ''
|
api_key = ''
|
||||||
headers = {
|
headers = {
|
||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
'Authorization': "" + api_key + ""
|
'Authorization': "" + api_key + ""
|
||||||
}
|
}
|
||||||
polling_time = 10
|
polling_time = 10
|
||||||
|
|
||||||
|
|
||||||
class Worker(Plugin.Plugin):
|
class Worker(Plugin.Plugin):
|
||||||
def __init__(self, name, params=None):
|
def __init__(self, name, params=None):
|
||||||
super(Worker, self).__init__(name, params)
|
super(Worker, self).__init__(name, params)
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
|
|
||||||
device = yun.YunGpio()
|
device = yun.YunGpio()
|
||||||
|
|
||||||
while (self._is_running):
|
while (self._is_running):
|
||||||
|
|
||||||
voltage = device._readVoltage("A0")
|
voltage = device._readVoltage("A0")
|
||||||
|
|
||||||
Rthermistor = float(Rb) * (float(ADCres) / float(voltage) - 1)
|
Rthermistor = float(Rb) * (float(ADCres) / float(voltage) - 1)
|
||||||
|
|
||||||
rel_temp = float(Beta) / (math.log(
|
rel_temp = float(Beta) / (math.log(
|
||||||
float(Rthermistor) * float(Ginf))
|
float(Rthermistor) * float(Ginf))
|
||||||
)
|
)
|
||||||
temp = rel_temp - Kelvin
|
temp = rel_temp - Kelvin
|
||||||
|
|
||||||
m_value = str(temp)
|
m_value = str(temp)
|
||||||
m_timestamp = datetime.datetime.now().strftime(
|
m_timestamp = datetime.datetime.now().strftime(
|
||||||
'%Y-%m-%dT%H:%M:%S.%f'
|
'%Y-%m-%dT%H:%M:%S.%f'
|
||||||
)
|
)
|
||||||
|
|
||||||
LOG.info(m_value + " - " + m_timestamp)
|
LOG.info(m_value + " - " + m_timestamp)
|
||||||
|
|
||||||
time.sleep(polling_time)
|
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.
|
# under the License.
|
||||||
|
|
||||||
from iotronic_lightningrod.plugins import Plugin
|
from iotronic_lightningrod.plugins import Plugin
|
||||||
|
# from iotronic_lightningrod.plugins import pluginApis as API
|
||||||
|
|
||||||
from oslo_log import log as logging
|
from oslo_log import log as logging
|
||||||
LOG = logging.getLogger(__name__)
|
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
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
|
__author__ = "Nicola Peditto <npeditto@unime.it"
|
||||||
|
|
||||||
import json
|
import json
|
||||||
|
|
||||||
SUCCESS = 'SUCCESS'
|
SUCCESS = 'SUCCESS'
|
||||||
|
|
|
@ -2,11 +2,11 @@
|
||||||
# of appearance. Changing the order has an impact on the overall integration
|
# of appearance. Changing the order has an impact on the overall integration
|
||||||
# process, which may cause wedges in the gate later.
|
# 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
|
# Openstack modules
|
||||||
oslo.config>=3.22.0 # Apache-2.0
|
oslo.config>=5.1.0 # Apache-2.0
|
||||||
oslo.log>=3.22.0 # Apache-2.0
|
oslo.log>=3.36.0 # Apache-2.0
|
||||||
|
|
||||||
autobahn>=0.10.1 # MIT License
|
|
||||||
httplib2>=0.7.5 # MIT
|
|
||||||
|
|
|
@ -16,8 +16,7 @@ classifier =
|
||||||
Programming Language :: Python :: 2
|
Programming Language :: Python :: 2
|
||||||
Programming Language :: Python :: 2.7
|
Programming Language :: Python :: 2.7
|
||||||
Programming Language :: Python :: 3
|
Programming Language :: Python :: 3
|
||||||
Programming Language :: Python :: 3.3
|
Programming Language :: Python :: 3.5
|
||||||
Programming Language :: Python :: 3.4
|
|
||||||
|
|
||||||
[files]
|
[files]
|
||||||
packages =
|
packages =
|
||||||
|
@ -58,3 +57,7 @@ s4t.modules =
|
||||||
utility = iotronic_lightningrod.modules.utils:Utility
|
utility = iotronic_lightningrod.modules.utils:Utility
|
||||||
plugin = iotronic_lightningrod.modules.plugin_manager:PluginManager
|
plugin = iotronic_lightningrod.modules.plugin_manager:PluginManager
|
||||||
device = iotronic_lightningrod.modules.device_manager:DeviceManager
|
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]
|
[tox]
|
||||||
minversion = 2.0
|
minversion = 2.3.1
|
||||||
envlist = py35,py27,pypy,pep8
|
envlist = py35,pep8
|
||||||
skipsdist = True
|
skipsdist = True
|
||||||
|
|
||||||
[testenv]
|
[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 =
|
setenv =
|
||||||
VIRTUAL_ENV={envdir}
|
VIRTUAL_ENV={envdir}
|
||||||
PYTHONWARNINGS=default::DeprecationWarning
|
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
|
deps = -r{toxinidir}/test-requirements.txt
|
||||||
commands = find . -type f -name "*.pyc" -delete
|
commands =
|
||||||
|
find . -type f -name "*.pyc" -delete
|
||||||
|
|
||||||
[testenv:pep8]
|
[testenv:pep8]
|
||||||
|
basepython = python2.7
|
||||||
commands = flake8 {posargs}
|
commands = flake8 {posargs}
|
||||||
|
|
||||||
[testenv:venv]
|
[testenv:venv]
|
||||||
|
@ -22,19 +29,26 @@ commands = {posargs}
|
||||||
commands = python setup.py test --coverage --testr-args='{posargs}'
|
commands = python setup.py test --coverage --testr-args='{posargs}'
|
||||||
|
|
||||||
[testenv:docs]
|
[testenv:docs]
|
||||||
commands = python setup.py build_sphinx
|
commands =
|
||||||
|
rm -fr doc/build
|
||||||
|
python setup.py build_sphinx
|
||||||
|
|
||||||
[testenv:releasenotes]
|
[testenv:releasenotes]
|
||||||
commands =
|
commands =
|
||||||
sphinx-build -a -E -W -d releasenotes/build/doctrees -b html releasenotes/source releasenotes/build/html
|
sphinx-build -a -E -W -d releasenotes/build/doctrees -b html releasenotes/source releasenotes/build/html
|
||||||
|
|
||||||
[testenv:debug]
|
[testenv:py35]
|
||||||
commands = oslo_debug_helper {posargs}
|
basepython = python3.5
|
||||||
|
|
||||||
|
|
||||||
[flake8]
|
[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.
|
# E123, E125 skipped as they are invalid PEP-8.
|
||||||
|
|
||||||
show-source = True
|
show-source = True
|
||||||
ignore = E123,E125
|
|
||||||
builtins = _
|
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