Diff of /URBasic/dataLogging.py [000000] .. [c1b1c5]

Switch to side-by-side view

--- a
+++ b/URBasic/dataLogging.py
@@ -0,0 +1,169 @@
+'''
+Python 3.x library to control an UR robot through its TCP/IP interfaces
+Copyright (C) 2017  Martin Huus Bjerge, Rope Robotics ApS, Denmark
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software
+and associated documentation files (the "Software"), to deal in the Software without restriction,
+including without limitation the rights to use, copy, modify, merge, publish, distribute,
+sublicense, and/or sell copies of the Software, and to permit persons to whom the Software
+is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all copies
+or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
+PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL "Rope Robotics ApS" BE LIABLE FOR ANY CLAIM,
+DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+Except as contained in this notice, the name of "Rope Robotics ApS" shall not be used
+in advertising or otherwise to promote the sale, use or other dealings in this Software
+without prior written authorization from "Rope Robotics ApS".
+'''
+
+__author__ = "Martin Huus Bjerge"
+__copyright__ = "Copyright 2017, Rope Robotics ApS, Denmark"
+__license__ = "MIT License"
+
+from pkg_resources import resource_filename
+import logging
+import time
+import os
+import re
+import URBasic
+import xml.etree.ElementTree as ET
+import ast
+from six import with_metaclass
+
+class Singleton(type):
+    _instances = {}
+    def __call__(self, *args, **kwargs):
+        if self not in self._instances:
+            self._instances[self] = super(Singleton, self).__call__(*args, **kwargs)
+        return self._instances[self]
+
+
+class DataLogging(with_metaclass(Singleton, object)):
+    '''
+    A module that add general logging functions to the UR Interface framework.
+    '''
+
+    def __init__(self, path=None, config=None):
+        '''
+        Constructor that setup a path where log files will be stored.
+        '''
+        self.directory = None
+        self.logDir = None
+
+        self.__developerTestingFlag = False
+        self.__eventLogFileMode = 'w'
+        self.__dataLogFileMode = 'w'
+
+        if config is None:
+            configFilename = resource_filename(__name__, 'logConfig.xml')
+        else:
+            configFilename = config
+        self.__readConfig(configFileName=configFilename)
+
+        self.GetLogPath(path=path, developerTestingFlag=self.__developerTestingFlag)
+
+
+
+        self.fileLogHandler = logging.FileHandler(os.path.join(self.directory, 'UrEvent.log'), mode=self.__eventLogFileMode)
+        self.fileLogHandler.setFormatter(logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s'))
+        self.streamLogHandler = logging.StreamHandler()
+        self.streamLogHandler.setFormatter(logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s'))
+        self.fileDataLogHandler = logging.FileHandler(os.path.join(self.directory, 'UrDataLog.csv'), mode=self.__dataLogFileMode)
+        self.writeDataLogHeadder = True
+
+
+
+    def __readConfig(self, configFileName):
+        tree = ET.parse(configFileName)
+        logConfig = tree.getroot()
+        developerModeTag = logConfig.find('developerMode')
+        self.__developerTestingFlag = ast.literal_eval(developerModeTag.text)
+
+        eventLogConfig = logConfig.find('eventLogConfig')
+        eventFileModeTag = eventLogConfig.find('fileMode')
+        if (eventFileModeTag.text == "Overwrite"):
+            self.__eventLogFileMode = 'w'
+        elif (eventFileModeTag.text == "Append"):
+            self.__eventLogFileMode = 'a'
+        else:
+            raise ValueError("Not supported eventLogfile mode: " + eventFileModeTag.text)
+
+        dataLogConfig = logConfig.find('dataLogConfig')
+        dataFileModeTag = dataLogConfig.find('fileMode')
+        if (dataFileModeTag.text == "Overwrite"):
+            self.__dataLogFileMode = 'w'
+        elif (dataFileModeTag.text == "Append"):
+            self.__dataLogFileMode = 'a'
+        else:
+            raise ValueError("Not supported dataLogfile mode: " + dataFileModeTag.text)
+
+
+
+    def GetLogPath(self,path=None, developerTestingFlag=True):
+        '''
+        Setup a path where log files will be stored
+        Path format .\[path]\YY-mm-dd\HH-MM-SS\
+        '''
+        if path is None:
+            path = URBasic.__file__[0:URBasic.__file__.find('URBasic')] + 'log'
+        else:
+            path = os.path.join(*(re.split('\\\\|/', path)))
+        if path[-1:]=='\\' or path[-1:]=='/':
+            path = path[0:-1]
+        if self.directory is None:
+            self.logDir = path
+            if developerTestingFlag:
+                self.directory = path
+            else:
+                self.directory =  os.path.join(path, time.strftime("%Y-%m-%d", time.localtime()), time.strftime("%H-%M-%S", time.localtime()))
+            if not os.path.exists(self.directory):
+                os.makedirs(self.directory)
+        return self.directory, self.logDir
+
+    def AddEventLogging(self, name='root', log2file=True, log2Consol=True, level = logging.WARNING):
+        '''
+        Add a new event logger, the event logger can log data to a file and also output the log to the console.
+
+        Input Parameters:
+        Name (str): The name of the logger the logger name will get the extension event
+        Log2file (bool): Set if the log should be stored in a log file
+        Log2Consol (bool): Set if the log should be output to the console
+
+        Return parameter:
+        Name (str): The logger name including the extension
+        '''
+        name = name.replace('__', '').replace('.', '_') + 'Event'
+        self.__dict__[name] = logging.getLogger(name)
+        if log2file:
+            self.__dict__[name].addHandler(self.fileLogHandler)
+        if log2Consol:
+            self.__dict__[name].addHandler(self.streamLogHandler)
+        self.__dict__[name].setLevel(level)
+        return name
+
+    def AddDataLogging(self,name='root'):
+        '''
+        Add a new data logger, the data logger will log data to a csv-file.
+
+        Input Parameters:
+        Name (str): The name of the logger the logger name will get the extension Data
+
+        Return parameter:
+        Name (str): The logger name including the extension
+        '''
+        name = name+'Data'
+        self.__dict__[name] = logging.getLogger(name)
+        self.__dict__[name].addHandler(self.fileDataLogHandler)
+        self.__dict__[name].setLevel(logging.WARNING)
+        if self.writeDataLogHeadder:
+            self.__dict__[name].info('Time;ModuleName;Level;Channel;UR_Time;Value1;Value2;Value3;Value4;Value5;Value6')
+            self.fileDataLogHandler.setFormatter(logging.Formatter('%(asctime)s;%(name)s;%(levelname)s;%(message)s'))
+            self.__dict__[name].addHandler(self.fileDataLogHandler)
+            self.writeDataLogHeadder = False
+        return name