Skip to content

Commit

Permalink
Re-wire config/layout classes
Browse files Browse the repository at this point in the history
  • Loading branch information
ty-porter committed Mar 11, 2024
1 parent 15d060b commit de00c7c
Show file tree
Hide file tree
Showing 18 changed files with 2,433 additions and 22 deletions.
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ venv.bak/
.mypy_cache/

# Ignore our customized config.json
/config*.json
config*.json
emulator_config.json

# Ignore any customized coordinate configs
Expand Down
52 changes: 52 additions & 0 deletions rewrite/config.json.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
{
"preferred": {
"teams": ["Cubs"],
"divisions": ["NL Central", "NL Wild Card"]
},
"news_ticker": {
"team_offday": true,
"always_display": false,
"preferred_teams": true,
"display_no_games_live": false,
"traderumors": true,
"mlb_news": true,
"countdowns": true,
"date": true,
"date_format": "%A, %B %-d"
},
"standings": {
"team_offday": false,
"mlb_offday": true,
"always_display": false,
"display_no_games_live": true
},
"rotation": {
"enabled": true,
"scroll_until_finished": true,
"only_preferred": false,
"only_live": true,
"rates": {
"live": 15.0,
"final": 15.0,
"pregame": 15.0
},
"while_preferred_team_live": {
"enabled": false,
"during_inning_breaks": false
}
},
"weather": {
"apikey": "YOUR_API_KEY_HERE",
"location": "Chicago,il,us",
"metric_units": false
},
"time_format": "12h",
"end_of_day": "00:00",
"full_team_names": true,
"short_team_names_for_runs_hits": true,
"preferred_game_update_delay_in_10s_of_seconds": 0,
"pregame_weather": true,
"scrolling_speed": 2,
"debug": false,
"demo_date": false
}
69 changes: 64 additions & 5 deletions rewrite/config/__init__.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,68 @@
from utils.font import FontCache
import json, os

from config.layout import Layout

from utils import logger as ScoreboardLogger
from utils import deep_update, read_json


class Config:
def __init__(self):
self.font_cache = FontCache()
def __init__(self, config_filename, width, height):
self.width = width
self.height = height
self.dimensions = (width, height)

config = self.__fetch_config(config_filename)
self.__parse_config(config)

self.layout = Layout(width, height)

def __fetch_config(self, name):
"""
Loads a config (JSON-formatted) with a custom filename. Falls back to a default if not found.
"""
filename = f"{name}.json"
reference_filename = f"config.json.example"

custom_config = read_json(filename)
reference_config = read_json(reference_filename)

if custom_config:
# Retain only the values that are valid.
config = deep_update(reference_config, custom_config)

return config

return reference_config

def __parse_config(self, config):
"""
Convert a JSON config file to callable attributes on the Config class.
If the key is nested, the top level key serves as its attribute prefix, i.e.:
{
"type": {
"key1": 1,
"key2": { "one": 1, "two": 2 }
}
}
config.type_key1
#=> 1
config.type_key2
#=> { "one": 1, "two": 2 }
If not nested, returns the result with no namespace, i.e.:
{ "type": "config" }
def font(self, font_name):
return self.font_cache.fetch_font(font_name)
config.type
#=> "config"
"""
for key in config:
if isinstance(config[key], dict):
for value in config[key]:
setattr(self, f"{key}_{value}", config[key][value])
else:
setattr(self, key, config[key])
55 changes: 55 additions & 0 deletions rewrite/config/layout.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import os, sys

from utils import logger as ScoreboardLogger
from utils.font import FontCache
from utils import deep_update, read_json


class Layout:
LAYOUT_DIRECTORY = os.path.abspath(os.path.join("../../coordinates"))
FONTNAME_DEFAULT = "4x6"

def __init__(self, width, height):
self.width = width
self.height = height

self.font_cache = FontCache(self.FONTNAME_DEFAULT)

self._json = self.__fetch_layout()

def font(self, font_name):
"""
Fetches a font from the font cache.
"""
return self.font_cache.fetch_font(font_name)

def font_size(self, font_name):
return self.font_cache.font_size(font_name)

@property
def default_font(self):
return self.font_cache.default_font

def __fetch_layout(self):
filename = "coordinates/w{}h{}.json".format(self.width, self.height)
reference_filename = "{}.example".format(filename)
reference_layout = read_json(reference_filename)
if not reference_layout:
# Unsupported coordinates
ScoreboardLogger.error(
"Invalid matrix dimensions provided. See top of README for supported dimensions."
"\nIf you would like to see new dimensions supported, please file an issue on GitHub!"
)
sys.exit(1)

# Load and merge any layout customizations
custom_layout = read_json(filename)
if custom_layout:
ScoreboardLogger.info(
"Custom '%dx%d.json' found. Merging with default reference layout.", self.width, self.height
)
layout = deep_update(reference_layout, custom_layout)

return layout

return reference_layout
38 changes: 38 additions & 0 deletions rewrite/coordinates/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
These JSON files are named in correspondence to the dimensions of the LED board used when running the software. A file, located in the `coordinates` directory with a filename `w<cols>h<rows>.json.example` tells the scoreboard that those dimensions are officially supported. This `.example` file is required and you will need to copy one of the existing files into a file that matches your dimensions.

# Custom Coordinates
You can edit these coordinates to display parts of the scoreboard in any way you choose. Simply copy the file corresponding to your board's dimensions to `w<cols>h<rows>.json`. This JSON file only needs to contain the parts you wish to override but it's often easier to just make a copy of the full example file and edit the values you want to change.

## Example
If you have a 64x32 board, copy `w64h32.json.example` to a new file called `w64h32.json`, then edit the coordinates in that file as you see fit. Your customized coordinates will always take precedence.

## Fonts
Any scoreboard element that prints text can accept a `"font_name"` attribute. Supported fonts need to be named with `<width>x<height>.bdf` (or `<width>x<height>B.bdf` for bold fonts). The font loader will search `assets/` first for the specified font and then it will fall back to searching `matrix/fonts/` if one was not found.

## States
The layout can have a couple of different states where things are rendered differently. Adding an object named for the layout state and giving it the same properties from the parent object will change the positioning of that parent object only when that state is found. For instance, when a game enters the `Warmup` state, the text `Warmup` appears under the time and the scrolling text is moved down.
* `warmup` will only render on the `pregame` screen and appears when a game enters the `Warmup` status. This usually happens 15-20 minutes before a game begins.
* `nohit` and `perfect_game` will only render on the live game screen and appears when a game returns that it is currently a no hitter or perfect game and the `innings_until_display` of `nohitter` has passed.
* The `runs_hits_errors` section enables the addition of hits and errors to the game screen.
* `show` turns this feature on or off.
* `compress_digits` will reduce the space between digits when the number of runs or hits is > 9.
* `spacing` is the number of pixels between the runs/hits and hits/errors.

## Pitch Data
* `enabled` (true/false) turn feature on/off
* `mph` (true/false) When rendering pitch speed add mph after (99 mph)
* `desc_length` (short/long) The short or long pitch type description, you can change both the short and long description to your liking in data/pitches as long as you do not change the index value.

## Play Result
* `enabled` (true/false) turn feature on/off
* `desc_length` (short/long) The short or long play result description. You can change both the short and long description to your liking in data/plays.

## Updates
The software develops and releases features with full support for the default layouts, so custom layouts may look unsatisfactory if you update to later versions of the scoreboard. If you as a user decide to create a custom layout file, you are responsible for tweaking the coordinates to your liking with each update.

## Current Issues
A couple of things are not completely implemented or have some implementation details you should understand.

* `bases` currently requires an even `size` value to be rendered correctly
* Not all options are enabled on all board sizes by default. For example pitch count and pitch type are not enabled by default on boards smaller than 64x64. Options are "disabled" by forcing them to render outside the board, by setting X and Y coordinates less than 0 or greater than the height or width of the board.

Loading

0 comments on commit de00c7c

Please sign in to comment.