diff --git a/.github/workflows/jekyll-gh-pages.yml b/.github/workflows/jekyll-gh-pages.yml new file mode 100644 index 0000000..9752d70 --- /dev/null +++ b/.github/workflows/jekyll-gh-pages.yml @@ -0,0 +1,74 @@ +# Workflow for building and deploying a Jekyll site to GitHub Pages +name: Deploy Jekyll with GitHub Pages dependencies preinstalled + +on: + # Runs on pushes targeting the default branch + push: + branches: ["main"] + + # Allows you to run this workflow manually from the Actions tab + workflow_dispatch: + +# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages +permissions: + contents: read + pages: write + id-token: write + +# Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued. +# However, do NOT cancel in-progress runs as we want to allow these production deployments to complete. +concurrency: + group: "pages" + cancel-in-progress: false + +jobs: + # Build job (github-pages-build) + build: + name: Build (github-pages-build gem) + runs-on: ubuntu-latest + env: + PAGES_REPO_NWO: ${{ github.repository }} + JEKYLL_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + JEKYLL_ENV: production + NODE_ENV: production + steps: + - name: Checkout Repository + uses: actions/checkout@v4 + - name: Setup environment + run: | + mv .jekyll-gh-pages/* . + - name: Setup Ruby + uses: ruby/setup-ruby@v1 + with: + ruby-version: "3.3" + bundler-cache: true # runs 'bundle install' to install and cache gems + - name: Add a YAML front matter to README.md + run: | + ex README.md <- - python -m - pip install - --upgrade pip - - name: Install dependencies - run: >- - python -m - pip install pyyaml python-daemon obd setuptools wheel twine - - name: Install pypa/build - run: >- - python -m - pip install - build - --user - - name: Build a binary wheel and a source tarball - run: >- - python setup.py sdist bdist_wheel - # This will create the /dist directory including the package build. + - name: Upgrade pip + run: pip install --upgrade pip - - name: Build and publish + - name: Install build dependencies + run: pip install setuptools wheel twine build + + - name: Build the package + run: python -m build + + - name: Publish to PyPI env: TWINE_USERNAME: ${{ secrets.PYPI_USERNAME }} TWINE_PASSWORD: ${{ secrets.PYPI_PASSWORD }} run: | - twine upload --repository pypi dist/* - twine upload --repository testpypi dist/* - # This will publish the /dist directory. + twine upload --repository pypi dist/*.tar.gz --verbose + + - name: Publish to TestPyPI + env: + TWINE_USERNAME: ${{ secrets.PYPI_USERNAME }} + TWINE_PASSWORD: ${{ secrets.TEST_PYPI_API_TOKEN }} + run: | + twine upload --repository testpypi dist/*.tar.gz --verbose diff --git a/.gitignore b/.gitignore index b9bacaf..32721be 100644 --- a/.gitignore +++ b/.gitignore @@ -136,3 +136,8 @@ Untitled*.ipynb comm_log.txt scantool.py mmap-input.bin + +# Just-The-Readme exclusions: +.jekyll-gh-pages/Gemfile.lock +.jekyll-gh-pages/_site/ +.jekyll-gh-pages/*.md diff --git a/.jekyll-gh-pages/Gemfile b/.jekyll-gh-pages/Gemfile new file mode 100644 index 0000000..8a6d93e --- /dev/null +++ b/.jekyll-gh-pages/Gemfile @@ -0,0 +1,20 @@ +source "https://rubygems.org" +gemspec + +gem "jekyll-github-metadata", ">= 2.15" + +gem "jekyll-include-cache", group: :jekyll_plugins +gem "jekyll-sitemap", group: :jekyll_plugins + +gem "html-proofer", "~> 5.0", :group => :development + +gem 'jekyll-autolinks' + +gem 'kramdown-parser-gfm' +gem "jekyll-remote-theme" + +#------------------------------------------------------------------------------------------------ +# After modifying the Gemfile: +#------------------------------------------------------------------------------------------------ +#bundle install +#bundle exec jekyll serve \ No newline at end of file diff --git a/.jekyll-gh-pages/_config.yml b/.jekyll-gh-pages/_config.yml new file mode 100644 index 0000000..b94a021 --- /dev/null +++ b/.jekyll-gh-pages/_config.yml @@ -0,0 +1,145 @@ +remote_theme: ircama/just-the-readme + +# Enable or disable the site search +# Supports true (default) or false +search_enabled: true + +# For copy button on code +enable_copy_code_button: true + +# Table of Contents +# Enable or disable the Table of Contents globally +# Supports true (default) or false +toc_enabled: true +toc: + # Minimum header level to include in ToC + # Default: 1 + min_level: 1 + # Maximum header level to include in ToC + # Default: 6 + max_level: 6 + # Display the ToC as ordered list instead of an unordered list + # Supports true (default) or false + ordered: true + # Whether ToC will be a single level list + # Supports true or false (default) + flat_toc: false + +# By default, consuming the theme as a gem leaves mermaid disabled; it is opt-in +mermaid: + # Version of mermaid library + # Pick an available version from https://cdn.jsdelivr.net/npm/mermaid/ + version: "9.1.6" + # Put any additional configuration, such as setting the theme, in _includes/mermaid_config.js + # See also docs/ui-components/code + # To load mermaid from a local library, also use the `path` key to specify the location of the library; e.g. + # for (v10+): + # path: "/assets/js/mermaid.esm.min.mjs" + # for (CC BY-NC-SA 4.0 license.' + +# Footer last edited timestamp +last_edit_timestamp: true # show or hide edit time - page must have `last_modified_date` defined in the frontmatter +last_edit_time_format: "%b %e %Y at %I:%M %p" # uses ruby's time format: https://ruby-doc.org/stdlib-2.7.0/libdoc/time/rdoc/Time.html + +# Footer "Edit this page on GitHub" link text +gh_edit_link: true # show or hide edit this page link +gh_edit_link_text: "Edit this page on GitHub" +gh_edit_repository: "https://github.com/Ircama/ELM327-emulator" # the github URL for your repo +gh_edit_branch: "main" # the branch that your docs is served from +# gh_edit_source: docs # the source that your files originate from +gh_edit_view_mode: "tree" # "tree" or "edit" if you want the user to jump into the editor immediately + +# Color scheme currently only supports "dark", "light"/nil (default), or a custom scheme that you define +color_scheme: nil + +callouts_level: quiet # or loud +callouts: + highlight: + color: yellow + important: + title: Important + color: blue + new: + title: New + color: green + note: + title: Note + color: purple + warning: + title: Warning + color: red + +# Google Analytics Tracking (optional) +# Supports a CSV of tracking ID strings (eg. "UA-1234567-89,G-1AB234CDE5") +# Note: the main Just the Docs site does *not* use Google Analytics. +# ga_tracking: UA-2709176-10,G-5FG1HLH3XQ +# ga_tracking_anonymize_ip: true # Use GDPR compliant Google Analytics settings (true/nil by default) + +plugins: + - jekyll-seo-tag + - jekyll-github-metadata + - jekyll-sitemap + - jekyll-autolinks + - jekyll-include-cache # Optional, for caching + - jekyll-remote-theme # Add if not already present + +kramdown: + syntax_highlighter_opts: + block: + line_numbers: false + +compress_html: + clippings: all + comments: all + endings: all + startings: [] + blanklines: false + profile: false + # ignore: + # envs: all + +autolinks: + link_attr: 'target="_blank"' + skip_tags: ["a", "pre", "code", "kbd", "script"] diff --git a/.jekyll-gh-pages/just-the-readme.gemspec b/.jekyll-gh-pages/just-the-readme.gemspec new file mode 100644 index 0000000..6723238 --- /dev/null +++ b/.jekyll-gh-pages/just-the-readme.gemspec @@ -0,0 +1,20 @@ +# coding: utf-8 + +Gem::Specification.new do |spec| + spec.name = "just-the-readme" + spec.version = "0.0.1" + spec.authors = ["Ircama"] + + spec.summary = %q{A modern, highly customizable, and responsive Jekyll theme for README documentation with built-in search.} + spec.homepage = "https://github.com/Ircama/just-the-readme" + spec.license = "MIT" + + spec.add_development_dependency "bundler", ">= 2.3.5" + spec.add_runtime_dependency "sass-embedded", "~> 1.78.0" # Fix use of deprecated sass lighten() and darken() + spec.add_runtime_dependency "jekyll", ">= 3.8.5" + spec.add_runtime_dependency "jekyll-seo-tag", ">= 2.0" + spec.add_runtime_dependency "jekyll-include-cache" + spec.add_runtime_dependency "rake", ">= 12.3.1" + spec.add_runtime_dependency "base64" + spec.add_runtime_dependency "csv" +end diff --git a/README.md b/README.md index a93b584..63c43eb 100644 --- a/README.md +++ b/README.md @@ -224,6 +224,7 @@ Command|Description `version`|Print ELM327-emulator version. With an argument, set the ELM version. If the argument is `hexheader` followed by a sequence of hex digits, the header of the ELM version is updated with the sequence. If the argument is `reset`, header and ELM version strings are set to default values. The autocompletion can be used with this command. `commands`|List the description of each available command. `choice`|Print or select the adopted method to choose the return value of answers that are expressed as a list of data. Possible values are "sequential" (the returned value follows the list sequence, which is the default mode) or "random" (the returned value is randomly selected within the values in the list). Optional list of weights can be added; decimals are allowed; the default value is 1. Autocompletion is allowed for this command. +`usage`|Open a web browser showing this documentation. In addition to the previously listed keywords, any Python command is allowed to query/configure the backend thread. diff --git a/elm/__version__.py b/elm/__version__.py index da4039b..d298347 100644 --- a/elm/__version__.py +++ b/elm/__version__.py @@ -1 +1 @@ -__version__ = '3.0.2' +__version__ = '3.0.3' diff --git a/elm/interpreter.py b/elm/interpreter.py index 91d598f..f1f46a6 100644 --- a/elm/interpreter.py +++ b/elm/interpreter.py @@ -9,6 +9,7 @@ import sys import traceback +import webbrowser import elm.elm @@ -49,7 +50,11 @@ from xml.etree.ElementTree import fromstring, ParseError, tostring import pprint except ImportError as detail: - print("ELM327 OBD-II adapter emulator error:\n " + str(detail)) + print( + "ELM327 OBD-II adapter emulator import error:\n " + + str(detail) + + "\nInstall prerequisites before running the product." + ) sys.exit(1) DAEMON_PIDFILE_DIR_ROOT = '/var/run/' @@ -231,7 +236,8 @@ def print_topics(self, header, cmds, cmdlen, maxcol): "Available commands include the following list (type help " "\nfor more information on each command). Besides, any Python" "\ncommand is accepted. Autocompletion is fully allowed." - "\nVisit https://github.com/Ircama/ELM327-emulator for additional info." + '\nFor additional info, type "usage" or visit' + "\nhttps://github.com/Ircama/ELM327-emulator" "\n==============================================================" "==\n") self.columnize(cmds, maxcol-1) @@ -271,10 +277,17 @@ def do_version(self, arg): "\nargument is 'reset', header and ELM version strings are set to "\ "default\nvalues." print(f'ELM327-emulator version {__version__}.') + if 'cmd_version' not in self.emulator.counters: + print( + "No access to counters. Start the emulator appropriately." + ) + return + print("Reset values:") if len(arg.split()) > 0 and arg.split()[0].lower() == "hexheader": if len(arg.split()) == 1: print( - "Missing the hex string following the 'hexheader' command.") + "Missing the hex string following the 'hexheader' command." + ) return try: self.emulator.header_version = "".join( @@ -285,7 +298,6 @@ def do_version(self, arg): return print("Set version header:") elif len(arg.split()) == 1 and arg.lower() == "reset": - print("Reset values:") self.emulator.counters['cmd_version'] = elm.elm.ELM_VERSION self.emulator.version = elm.elm.ELM_VERSION self.emulator.header_version = elm.elm.ELM_HEADER_VERSION @@ -463,6 +475,11 @@ def do_timer(self, arg): args = arg.split() usage = ( 'Usage: timer {P1|P2|P3|P4} seconds; ref. "help timer" command.') + if 'req_timeout' not in self.emulator.counters: + print( + "No access to counters. Start the emulator appropriately." + ) + return if not args: print ("P1: {} seconds " "- UDS P1 timer - Inter byte time for ECU response".format( @@ -584,8 +601,13 @@ def do_resume(self, arg): print ("Invalid format.") return self.emulator.threadState = self.emulator.THREAD.ACTIVE - print( - "Backend emulator resumed. Running on %s" % self.emulator.get_pty()) + if self.emulator.get_pty(): + print( + "Backend emulator resumed. Running on %s" + % self.emulator.get_pty() + ) + else: + print("Missing connection to serial port.") def complete_loglevel(self, text, line, start_index, end_index): if text: @@ -668,7 +690,10 @@ def do_edit(self, arg): return if Edit.answer(self, position, ''.join(args[2:]), args[0]): print(f'Set answer for Pid {args[0]} with edited bytes:') - print(self.emulator.answer[args[0]]) + if args[0] in self.emulator.answer: + print(self.emulator.answer[args[0]]) + else: + print("Unknown value") else: print(f'Cannot set answer for pid {args[0]}.') return @@ -773,6 +798,21 @@ def do_commands(self, arg): "\n", formatter.format('')))) print("") + def do_usage(self, arg): + "Open a web browser showing the online documentation." + url = "https://ircama.github.io/ELM327-emulator" + if arg: + print ("Invalid format of the command.") + return + try: + ret = webbrowser.open(url) + if ret: + print("The web browser is being opened.") + else: + print("Cannot open the web browser.") + except Exception as e: + print("Cannot open the web browser:", e) + def do_history(self, arg): "print the command history; if an argument is given, print the last\n"\ "n commands in the history; with argument 'clear', clears the history." @@ -923,6 +963,10 @@ def set_scenario(emulator, scenario): def main(): # Option handling + NO_PTY = ( + "\nThe ELM327-emulator is started locally," + " without serial port connection and with limited capabilities.\n" + ) parser = argparse.ArgumentParser( epilog='ELM327-emulator v' + __version__ + ' - ELM327 OBD-II adapter emulator') @@ -1205,14 +1249,14 @@ def main(): pty_name = "TCP network port " + str(args.net_port[0]) + "." else: pty_name = session.get_pty() - if args.batch_mode: - print(pty_name) - sys.stdout.flush() + if pty_name == None: + print(NO_PTY) + else: + if args.batch_mode: + print(pty_name) + sys.stdout.flush() if args.scenario[0]: set_scenario(session, args.scenario[0]) - if pty_name == None: - print("\nCannot start ELM327-emulator.\n") - os._exit(1) # does not raise SystemExit p_elm = Interpreter(session, args) if args.log: logging.getLogger().handlers[0].setLevel(int(args.log[0])) @@ -1221,10 +1265,17 @@ def main(): 'ELM327-emulator batch mode STARTED\n' 'Begin batch commands.') else: - p_elm.cmdloop_with_keyboard_interrupt( - 'Welcome to the ELM327 OBD-II adapter emulator.\n' - 'ELM327-emulator is running on %s\n' - 'Type help or ? to list commands.\n' % pty_name) + if pty_name == None: + p_elm.cmdloop_with_keyboard_interrupt( + 'Welcome to the ELM327 OBD-II adapter emulator.\n' + 'Type help or ? to list commands.\n' + ) + else: + p_elm.cmdloop_with_keyboard_interrupt( + 'Welcome to the ELM327 OBD-II adapter emulator.\n' + 'ELM327-emulator is running on %s\n' + 'Type help or ? to list commands.\n' % pty_name + ) except (KeyboardInterrupt, SystemExit) as e: if not args.batch_mode and p_elm: p_elm.postloop()