Import LinuxCNC source tree

This commit is contained in:
Wang Dequan
2026-08-20 06:17:39 +08:00
commit 2c814634c3
9429 changed files with 10556013 additions and 0 deletions

View File

@@ -0,0 +1,213 @@
#!/usr/bin/env python3
# Copyright (c) 2017 Kurt Jacobson
# 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 THE
# AUTHORS OR COPYRIGHT HOLDERS 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.
import re
from copy import copy
from logging import Formatter
PREFIX = '\033['
SUFFIX = '\033[0m'
COLORS = {
'black': 30,
'red': 31,
'green': 32,
'yellow': 33,
'blue': 34,
'magenta': 35,
'cyan': 36,
'white': 37,
'bgred': 41,
'bggreen': 42,
'bgyellow': 43,
'bggrey': 100
}
MAPPING = {
'VERBOSE': 'bggrey',
'DEBUG': 'blue',
'INFO': 'cyan',
'WARNING': 'yellow',
'ERROR': 'red',
'CRITICAL': 'bgred',
}
KEY_SET = set(list(COLORS))
# Returns `text` wrapped in ASCII escape codes to produce `color`
def COLORIZE(text, color=None):
seq = COLORS.get(color, 37) # default to white
return '{0}{1}m{2}{3}'.format(PREFIX, seq, text, SUFFIX)
# Returns `text` wrapped in Qt Richtext Html codes to produce `color`
def QTCOLORIZE(text, color=None):
if color in('bgred','bggreen','bgyellow','bggrey'):
color = color.replace('bg','')
return f'<span style="background-color: {color};">{text}</span>'
return f'<font color="{color}">{text}</font>'
# Matches only the first `color<text>` occurrence
# ^(.*?)<([^)]+)>
# Matches all `color<text>` occurrences, both take the same number of steps
# ([^<\s]+)<([^>]+)>
# (\w+)<([^>]+)>
RE = re.compile(r'(\w+)<([^>]+)>')
class ColoredFormatter(Formatter):
def __init__(self, pattern, pass_clean=True):
self.pass_clean = pass_clean
Formatter.__init__(self, pattern)
# Override the Formatter's format method to add ASCII colors
# to the levelname and any marked words in the log message.
def format(self, record):
colored_record = copy(record)
# Add colors to levelname
levelname = colored_record.levelname
color = MAPPING.get(levelname, 'white')
colored_record.levelname = COLORIZE(levelname, color)
# Add colors to tagged message text
msg = colored_record.getMessage()
plain_msg, color_msg = self.color_words(msg)
# if next logger will colorize too
# then don't clean color codes from msg
if self.pass_clean:
record.msg = plain_msg
colored_record.msg = color_msg
return Formatter.format(self, colored_record)
# Replace `color<message>` in the log message with ASCII codes to colorize
# the word or phrase in the terminal log handler. Also return a cleaned
# version of the message with the tags removed for use by the file handler.
def color_words(self, raw_msg):
# Function to handle color replacement
def color_handler(match):
group = match.group(0) # The whole thing: <red>hello
color = match.group(1) # Group 1: red
word = match.group(2) # Group 2: hello
if color in KEY_SET: # must be one of the color key words
return COLORIZE(word, color)
return group # If color is invalid, just return the word without tags
# Function to handle plain text (stripping tags)
def plain_handler(match):
return match.group(2) # Just return the word part
# Perform both replacements in one pass each
color_msg = RE.sub(color_handler, raw_msg)
plain_msg = RE.sub(plain_handler, raw_msg)
return plain_msg, color_msg
class QtColoredFormatter(Formatter):
def __init__(self, pattern, pass_clean=True):
self.pass_clean = pass_clean
Formatter.__init__(self, pattern)
# Override the Formatter's format method to add ASCII colors
# to the levelname and any marked words in the log message.
def format(self, record):
colored_record = copy(record)
# Add colors to levelname
levelname = colored_record.levelname
color = MAPPING.get(levelname, 'white')
colored_record.levelname = QTCOLORIZE(levelname, color)
# Add colors to tagged message text
msg = colored_record.getMessage()
plain_msg, color_msg = self.qtcolor_words(msg)
# if next logger will colorize too
# then don't clean color codes from msg
if self.pass_clean:
record.msg = plain_msg
colored_record.msg = color_msg
return Formatter.format(self, colored_record)
# Replace `color<message>` in the log message with Html codes to colorize
def qtcolor_words(self, raw_msg):
# Function to handle color replacement
def color_handler(match):
group = match.group(0) # The whole thing: <red>hello
color = match.group(1) # Group 1: red
word = match.group(2) # Group 2: hello
if color in KEY_SET: # must be one of the color key words
return QTCOLORIZE(word, color)
return group # If color is invalid, just return the word without tags
# Function to handle plain text (stripping tags)
def plain_handler(match):
return match.group(2) # Just return the word part
# Perform both replacements in one pass each
color_msg = RE.sub(color_handler, raw_msg)
plain_msg = RE.sub(plain_handler, raw_msg)
return plain_msg, color_msg
# ----------------------- E X A M P L E -----------------------
if __name__ == '__main__':
import logging
# Create logger
log = logging.getLogger(__name__)
log.setLevel(logging.DEBUG)
# Add console handler
ch = logging.StreamHandler()
ch.setLevel(logging.DEBUG)
cf = ColoredFormatter("[%(name)s][%(levelname)s] %(message)s (%(filename)s:%(lineno)d)")
ch.setFormatter(cf)
log.addHandler(ch)
# Add file handler
fh = logging.FileHandler('demo.log')
fh.setLevel(logging.DEBUG)
ff = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
fh.setFormatter(ff)
log.addHandler(fh)
# Log some stuff
log.debug("Logging demo has green<STARTED>")
log.info("Logging to yellow<demo.log> in the script dir")
log.warning("This is my last warning, red<take heed>")
log.error("This is an error")
log.critical("He's dead, Jim")
# Example exception logging
try:
print(False + "True")
except Exception as e:
log.debug('That did not work!', exc_info=e)

File diff suppressed because it is too large Load Diff

1035
lib/python/common/iniinfo.py Normal file

File diff suppressed because it is too large Load Diff

145
lib/python/common/logger.py Executable file
View File

@@ -0,0 +1,145 @@
#!/usr/bin/env python3
# Common GUI Logging Module
# Provides a consistent and easy to use logging facility. Log messages printed
# to the terminal will be colorized for easy identification of log level.
#
# Copyright (c) 2017 Kurt Jacobson
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
import os
import logging
from linuxcnc import ini
# For convenience import log levels so we don't need to import
# logging to set the log level within other modules.
from logging import DEBUG, INFO, WARNING, ERROR, CRITICAL
# add a new verbose level
VERBOSE = 5
logging.VERBOSE = 5
logging.addLevelName(logging.VERBOSE, "VERBOSE")
# add a custom log level function
def verbose(self, message, *args, **kws):
if self.isEnabledFor(VERBOSE):
# Yes, logger takes its '*args' as 'args'.
self._log(logging.VERBOSE, message, args, **kws)
# add the custom log level to the library (class patch)
logging.Logger.verbose = verbose
logging.Logger.VERBOSE = VERBOSE
# Our custom colorizing formatters for the terminal/file handler
from .colored_formatter import ColoredFormatter, QtColoredFormatter
# Global name of the base logger
BASE_LOGGER_NAME = None
BASE_LOGGER_FILE = None
# Define the log message formats
TERM_FORMAT = '[%(name)s][%(levelname)s] %(message)s (%(filename)s:%(lineno)d)'
FILE_FORMAT = '%(asctime)s - %(name)s - %(levelname)s - %(message)s'
QTFILE_FORMAT = '%(levelname)s - %(message)s (%(filename)s:%(lineno)d)<br>'
# Get logger for module based on module.__name__
def getLogger(name):
if BASE_LOGGER_NAME is None:
initBaseLogger('DEFAULT')
name = '{0}.{1}'.format(BASE_LOGGER_NAME, name.upper())
return logging.getLogger(name)
# Set global logging level
def setGlobalLevel(level):
base_log = logging.getLogger(BASE_LOGGER_NAME)
base_log.setLevel(level)
# Initialize the base logger
def initBaseLogger(name, log_file=None, log_level=DEBUG, logToFile=False):
global BASE_LOGGER_NAME
BASE_LOGGER_NAME = name
if not log_file:
log_file = getLogFile(name)
global BASE_LOGGER_FILE
BASE_LOGGER_FILE = log_file
# Clear the previous sessions log file
with open(log_file, 'w') as fh:
pass
# Create base logger
base_log = logging.getLogger(BASE_LOGGER_NAME)
base_log.setLevel(log_level)
# Add console handler
ch = logging.StreamHandler()
ch.setLevel(logging.VERBOSE)
# is this a Qtvcp logger?
Qt = bool( 'qt' in name.lower())
cf = ColoredFormatter(TERM_FORMAT, pass_clean = not Qt)
ch.setFormatter(cf)
base_log.addHandler(ch)
# Add file handler
if logToFile:
fh = logging.FileHandler(log_file)
fh.setLevel(logging.VERBOSE)
# if Qtvcp, use Qt Rich text colors
# else use plain text
if Qt:
ff = QtColoredFormatter(QTFILE_FORMAT)
else:
ff = logging.Formatter(FILE_FORMAT)
fh.setFormatter(ff)
base_log.addHandler(fh)
# Get logger for logger
log = getLogger(__name__)
if logToFile:
base_log.info('Logging to: yellow<{}>'.format(log_file))
return base_log
# Attempt to find the log file specified INI [DISPLAY] LOG_FILE,
# failing that log to $HOME/<base_log_name>.log
def getLogFile(name):
# Default log file to use if not specified in INI
log_file = os.path.expanduser('~/{}.log').format(name.lower())
# LinuxCNC may not be running, so use get() to avoid a KeyError
ini_file = os.environ.get('INI_FILE_NAME')
config_dir = os.environ.get('CONFIG_DIR')
if ini_file:
lcnc_ini = ini(ini_file)
path = lcnc_ini.find('DISPLAY', 'LOG_FILE')
if path:
if path.startswith('~'):
# Path is relative to $HOME
log_file = os.path.expanduser(path)
elif not os.path.isabs(path):
# Assume intended path is relative to the INI file
log_file = os.path.join(config_dir, path)
else:
# It must be an absolute path then
log_file = os.path.realpath(path)
return log_file