Files
ThingsBoard/RPi Docker/python_firmware/drivers/dia.py
2024-10-04 18:53:54 -05:00

371 lines
13 KiB
Python

#!/usr/bin/python
############################################################################
# #
# Copyright (c)2008, 2009, Digi International (Digi). All Rights Reserved. #
# #
# Permission to use, copy, modify, and distribute this software and its #
# documentation, without fee and without a signed licensing agreement, is #
# hereby granted, provided that the software is used on Digi products only #
# and that the software contain this copyright notice, and the following #
# two paragraphs appear in all copies, modifications, and distributions as #
# well. Contact Product Management, Digi International, Inc., 11001 Bren #
# Road East, Minnetonka, MN, +1 952-912-3444, for commercial licensing #
# opportunities for non-Digi products. #
# #
# DIGI SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT LIMITED #
# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A #
# PARTICULAR PURPOSE. THE SOFTWARE AND ACCOMPANYING DOCUMENTATION, IF ANY, #
# PROVIDED HEREUNDER IS PROVIDED "AS IS" AND WITHOUT WARRANTY OF ANY KIND. #
# DIGI HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, #
# ENHANCEMENTS, OR MODIFICATIONS. #
# #
# IN NO EVENT SHALL DIGI BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT, #
# SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING LOST PROFITS, #
# ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF #
# DIGI HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. #
# #
############################################################################
import time
import os
try:
import json
except:
import simplejson as json
import thread
import threading
class start(threading.Thread):
def __init__(self, name=None, number=None, mac=None, Q=None, mcu=None, companyId=None, offset=None, mqtt=None, Nodes=None):
threading.Thread.__init__(self)
self.daemon = True
self.offset = offset
self.company = companyId
self.name = name
self.number = number
self.q = Q
self.deviceName = name + '_[' + mac + ':' + number[0:2] + ':' + number[2:] + ']!'
print 'device name is:'
print self.deviceName
mac2 = mac.replace(":", "")
self.mac = mac2.upper()
self.version = "5"
self.finished = threading.Event()
self.nodes = Nodes
self.mqtt = mqtt
self.core = None
threading.Thread.start(self)
#this is a required function for all drivers, its goal is to upload some piece of data
#about your device so it can be seen on the web
def register(self):
pass
#self.mainMistaway_hb('hb', 'On')
def stop (self):
self.finished.set()
self.join()
def sendtodb(self, channel, value, timestamp):
if int(timestamp) == 0:
timestamp = self.getTime()
if timestamp < 1400499858:
return
try:
topic = 'meshify/db/%s/%s/%s/%s' % (self.company, self.mac, self.deviceName, channel)
print topic
if channel == "files":
#for the file structure I had to take off the " " around the value
msg = """[ { "value":%s, "timestamp":"%s" } ]""" % (str(value), str(timestamp))
else:
msg = """[ { "value":"%s", "timestamp":"%s" } ]""" % (str(value), str(timestamp))
print msg
self.q.put([topic, msg, 0])
except:
print "didn't work to send up MQTT data"
def run(self):
#on startup send the version number
self.sendtodb("version", str(self.version), 0)
core = main()
self.core = core
#wait some time
core.set_service("master", self)
spin_forever(core)
def getTime(self):
return str(int(time.time() + int(self.offset)))
"""\
To run this, use command line: python dia.py [config.yml]
"""
# imports
import os
import sys
try:
import os.path
except ImportError:
# os comes from python.zip on our gateways.
print """\
Unable to import 'os' module. Check that your system has a 'python.zip' file,\r
and that it is the correct one for your device.\r
"""
sys.exit(1)
import gc
import time
# constants
BOOTSTRAP_VERSION = "2.1.0.8"
GC_COLLECTION_INTERVAL = 60 * 15 # fifteen minutes
# internal functions & classes
def spin_forever(core):
""" This routine prevents the main thread from exiting when the
framework is run directly from __main__.
"""
try:
while not core.shutdown_requested():
collected_items = gc.collect()
if collected_items:
# only print if something accomplished
print ("GarbageCollector: collected %d objects."
%collected_items)
core.wait_for_shutdown(GC_COLLECTION_INTERVAL)
finally:
core._shutdown()
print "dia.py is exiting...\n"
def setup_path_and_zip():
""" Sets up the paths to import from the appropriate locations.
Also detects if a zip file is present and returns the path to the
archive
"""
#Does the dia.zip exist in our local directory?
expected_zip_path = os.path.join(os.path.abspath('.'), 'dia.zip')
if os.path.exists(expected_zip_path):
#Yes, add it to the path:
sys.path.append(expected_zip_path)
#Add the paths internal to the zip file
for lib_path in ['lib', 'src']:
sys.path.insert(0, os.path.join(expected_zip_path, lib_path))
return expected_zip_path
else:
#We may be operating in a environment that doesn't need dia.zip
#find files, like /src/core/core_services.py
if not os.path.exists(os.path.join('src', 'core', 'core_services.py')):
raise RuntimeError("Unable to find dia.zip or core libraries"
", please load dia.zip and try again")
else:
#We're running in the root of the source tree, directly add the
#/src and /lib directories, insert them, so they are imported
#before base libraries, in case of conflict
for lib_path in ['lib', 'src']:
sys.path.insert(0, lib_path)
return None
def locate_configuration_file(expected_zip_path):
""" Locates the settings file, returns the settings in a file like object,
returns the source name of the settings (the file name used),
and returns the destination of where the settings could be saved.
The source and destination will differ if the source is the dia.zip file.
"""
settings_file = None
settings_flo = None
dest_file = None
#To locate the settings file, parse command line
if sys.argv and len(sys.argv) > 1:
#Use the one supplied by the args
settings_file = sys.argv[1]
if not os.path.exists(settings_file):
#Try again for NDS, doesn't have concept of local directory
settings_file = os.path.abspath(settings_file)
if not os.path.exists(settings_file):
raise RuntimeError("Settings file: %s given, but not found"
" in path" %(settings_file))
#settings file found, read it in
settings_flo = open(settings_file, 'r')
dest_file = settings_file
else:
#No direction from args to find settings file
#Search local path for dia.pyr, dia.yml in order
for possible_fname in ['dia.pyr', 'dia.yml']:
if os.path.exists(os.path.abspath(possible_fname)):
dest_file = settings_file = os.path.abspath(possible_fname)
settings_flo = open(settings_file, 'r')
break
if (expected_zip_path is not None) and (settings_flo is None):
#Previous attempts at finding the configuration are unsuccessful
#Search the dia.zip archive for the settings file, dia.pyr/dia.yml
import zipfile
import StringIO
dia_zip_flo = open(expected_zip_path, 'rb')
dia_zip = zipfile.ZipFile(dia_zip_flo)
#Find the fname, then break
for possible_fname in ['dia.pyr', 'dia.yml']:
if possible_fname in dia_zip.namelist():
#Set the flo object to the file buffer in the zip file
#Provide an alternative location, we cannot overwrite the
#configuration in the zipfile
settings_file = os.path.join(expected_zip_path, possible_fname)
dest_file = os.path.join(os.path.abspath('.'), possible_fname)
settings_flo = StringIO.StringIO(dia_zip.read(possible_fname))
dia_zip_flo.close()
dia_zip.close()
break
return settings_file, settings_flo, dest_file
def do_slowdown_check():
""" Performs the slow down check and if true, slows down the startup of
the Dia. This is done to prevent platforms that have an auto restart on
exit of the Dia from spinning fast enough to prevent modification of the
platform if something causes the startup to fail immediately.
"""
#Check for the file that enables this feature
stop_fname = os.path.join(os.path.abspath("."), "nospin.txt")
if not os.path.exists(stop_fname):
print "Dia auto-detect of rapid reboot DISABLED"
return
print "Dia auto-detect of rapid reboot ENABLED"
#Continue, check the timestamp file for entries
slowdown = False
ts_file = open(stop_fname, 'r')
entries = ts_file.readlines()
ts_file.close()
##Remove all non-float compatible entries
for ent in entries[:]:
try:
float(ent)
except ValueError:
entries.remove(ent)
#If more than 9 entries, find average of last 10 entries
if len(entries) >= 10:
curr_time = time.time()
avg_time = sum(float(x.strip()) for x in entries[-10:]) / 10.0
#Compare them to the current time, and if less than 20 minutes
#mark us ready for slow down
diff_time = curr_time - avg_time
if diff_time < 1200:
print ("Initiating slow down in order to prevent the Dia from "
"spinning too fast")
slowdown = True
#Cycle out old timestamps
entries.append(str(time.time()) + os.linesep)
ts_file = open(stop_fname, "w")
ts_file.writelines(entries[-10:])
ts_file.close()
#If need to slow down, do so here
if slowdown:
print "Slowing down, pausing for 10 minutes"
time.sleep(600)
print "Done slowing down."
def main():
os.chdir('/root/python_firmware/drivers')
""" Acts as the startup script for the Dia. Sets up the environment
(sys.path) and loads the configuration file for use by the core services
to load the rest of the system
"""
#Perform slow down check in case of reboot cycle
do_slowdown_check()
#File name of the settings being used
settings_file = None
#File object derived from opening the settings file
settings_flo = None
#If using a zip file, will return path to zip
expected_zip_path = setup_path_and_zip()
#We've found the library files, verify matching version
try:
from src.common.dia_version import DIA_VERSION
except Exception:
if expected_zip_path:
print ("Error reading from Zipfile: %s Common cause for error"
"is that the files inside the zip file were compiled with "
"the incorrect version of python." %expected_zip_path)
else:
print ("No dia.zip found and unable to locate the file "
"src/common/dia_version.py in the local path.")
raise
if DIA_VERSION != BOOTSTRAP_VERSION:
raise RuntimeError("Library files found, but bootstrap and library "
"versions do not match! Expected: %s, Found: %s"
%(BOOTSTRAP_VERSION, DIA_VERSION))
#Locate the settings file that we use for starting up
settings_file, settings_flo, dest_file = \
locate_configuration_file(expected_zip_path)
if settings_file is None or settings_flo is None:
raise RuntimeError("Unable to locate settings file in local directory,"
" from command line input, or dia.zip. Please "
"provide a configuration file in one of these "
"locations.")
print "Running in environment: %s" % sys.platform
print "iDigi Device Integration Application Version %s" % DIA_VERSION
print "Source settings file: %s" % settings_file
print "Destination settings file: %s" % dest_file
from core.core_services import CoreServices
core = CoreServices(settings_flo=settings_flo,
settings_filename=dest_file)
#if __name__ == "__main__":
# Don't exit. If not __main__ then the caller needs to guarantee this.
#spin_forever(core)
return core