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

Switch to unified view

a b/URBasic/dataLogging.py
1
'''
2
Python 3.x library to control an UR robot through its TCP/IP interfaces
3
Copyright (C) 2017  Martin Huus Bjerge, Rope Robotics ApS, Denmark
4
5
Permission is hereby granted, free of charge, to any person obtaining a copy of this software
6
and associated documentation files (the "Software"), to deal in the Software without restriction,
7
including without limitation the rights to use, copy, modify, merge, publish, distribute,
8
sublicense, and/or sell copies of the Software, and to permit persons to whom the Software
9
is furnished to do so, subject to the following conditions:
10
11
The above copyright notice and this permission notice shall be included in all copies
12
or substantial portions of the Software.
13
14
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
15
INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
16
PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL "Rope Robotics ApS" BE LIABLE FOR ANY CLAIM,
17
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
19
20
Except as contained in this notice, the name of "Rope Robotics ApS" shall not be used
21
in advertising or otherwise to promote the sale, use or other dealings in this Software
22
without prior written authorization from "Rope Robotics ApS".
23
'''
24
25
__author__ = "Martin Huus Bjerge"
26
__copyright__ = "Copyright 2017, Rope Robotics ApS, Denmark"
27
__license__ = "MIT License"
28
29
from pkg_resources import resource_filename
30
import logging
31
import time
32
import os
33
import re
34
import URBasic
35
import xml.etree.ElementTree as ET
36
import ast
37
from six import with_metaclass
38
39
class Singleton(type):
40
    _instances = {}
41
    def __call__(self, *args, **kwargs):
42
        if self not in self._instances:
43
            self._instances[self] = super(Singleton, self).__call__(*args, **kwargs)
44
        return self._instances[self]
45
46
47
class DataLogging(with_metaclass(Singleton, object)):
48
    '''
49
    A module that add general logging functions to the UR Interface framework.
50
    '''
51
52
    def __init__(self, path=None, config=None):
53
        '''
54
        Constructor that setup a path where log files will be stored.
55
        '''
56
        self.directory = None
57
        self.logDir = None
58
59
        self.__developerTestingFlag = False
60
        self.__eventLogFileMode = 'w'
61
        self.__dataLogFileMode = 'w'
62
63
        if config is None:
64
            configFilename = resource_filename(__name__, 'logConfig.xml')
65
        else:
66
            configFilename = config
67
        self.__readConfig(configFileName=configFilename)
68
69
        self.GetLogPath(path=path, developerTestingFlag=self.__developerTestingFlag)
70
71
72
73
        self.fileLogHandler = logging.FileHandler(os.path.join(self.directory, 'UrEvent.log'), mode=self.__eventLogFileMode)
74
        self.fileLogHandler.setFormatter(logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s'))
75
        self.streamLogHandler = logging.StreamHandler()
76
        self.streamLogHandler.setFormatter(logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s'))
77
        self.fileDataLogHandler = logging.FileHandler(os.path.join(self.directory, 'UrDataLog.csv'), mode=self.__dataLogFileMode)
78
        self.writeDataLogHeadder = True
79
80
81
82
    def __readConfig(self, configFileName):
83
        tree = ET.parse(configFileName)
84
        logConfig = tree.getroot()
85
        developerModeTag = logConfig.find('developerMode')
86
        self.__developerTestingFlag = ast.literal_eval(developerModeTag.text)
87
88
        eventLogConfig = logConfig.find('eventLogConfig')
89
        eventFileModeTag = eventLogConfig.find('fileMode')
90
        if (eventFileModeTag.text == "Overwrite"):
91
            self.__eventLogFileMode = 'w'
92
        elif (eventFileModeTag.text == "Append"):
93
            self.__eventLogFileMode = 'a'
94
        else:
95
            raise ValueError("Not supported eventLogfile mode: " + eventFileModeTag.text)
96
97
        dataLogConfig = logConfig.find('dataLogConfig')
98
        dataFileModeTag = dataLogConfig.find('fileMode')
99
        if (dataFileModeTag.text == "Overwrite"):
100
            self.__dataLogFileMode = 'w'
101
        elif (dataFileModeTag.text == "Append"):
102
            self.__dataLogFileMode = 'a'
103
        else:
104
            raise ValueError("Not supported dataLogfile mode: " + dataFileModeTag.text)
105
106
107
108
    def GetLogPath(self,path=None, developerTestingFlag=True):
109
        '''
110
        Setup a path where log files will be stored
111
        Path format .\[path]\YY-mm-dd\HH-MM-SS\
112
        '''
113
        if path is None:
114
            path = URBasic.__file__[0:URBasic.__file__.find('URBasic')] + 'log'
115
        else:
116
            path = os.path.join(*(re.split('\\\\|/', path)))
117
        if path[-1:]=='\\' or path[-1:]=='/':
118
            path = path[0:-1]
119
        if self.directory is None:
120
            self.logDir = path
121
            if developerTestingFlag:
122
                self.directory = path
123
            else:
124
                self.directory =  os.path.join(path, time.strftime("%Y-%m-%d", time.localtime()), time.strftime("%H-%M-%S", time.localtime()))
125
            if not os.path.exists(self.directory):
126
                os.makedirs(self.directory)
127
        return self.directory, self.logDir
128
129
    def AddEventLogging(self, name='root', log2file=True, log2Consol=True, level = logging.WARNING):
130
        '''
131
        Add a new event logger, the event logger can log data to a file and also output the log to the console.
132
133
        Input Parameters:
134
        Name (str): The name of the logger the logger name will get the extension event
135
        Log2file (bool): Set if the log should be stored in a log file
136
        Log2Consol (bool): Set if the log should be output to the console
137
138
        Return parameter:
139
        Name (str): The logger name including the extension
140
        '''
141
        name = name.replace('__', '').replace('.', '_') + 'Event'
142
        self.__dict__[name] = logging.getLogger(name)
143
        if log2file:
144
            self.__dict__[name].addHandler(self.fileLogHandler)
145
        if log2Consol:
146
            self.__dict__[name].addHandler(self.streamLogHandler)
147
        self.__dict__[name].setLevel(level)
148
        return name
149
150
    def AddDataLogging(self,name='root'):
151
        '''
152
        Add a new data logger, the data logger will log data to a csv-file.
153
154
        Input Parameters:
155
        Name (str): The name of the logger the logger name will get the extension Data
156
157
        Return parameter:
158
        Name (str): The logger name including the extension
159
        '''
160
        name = name+'Data'
161
        self.__dict__[name] = logging.getLogger(name)
162
        self.__dict__[name].addHandler(self.fileDataLogHandler)
163
        self.__dict__[name].setLevel(logging.WARNING)
164
        if self.writeDataLogHeadder:
165
            self.__dict__[name].info('Time;ModuleName;Level;Channel;UR_Time;Value1;Value2;Value3;Value4;Value5;Value6')
166
            self.fileDataLogHandler.setFormatter(logging.Formatter('%(asctime)s;%(name)s;%(levelname)s;%(message)s'))
167
            self.__dict__[name].addHandler(self.fileDataLogHandler)
168
            self.writeDataLogHeadder = False
169
        return name