A Go-based REST API server that connects to MeOS (orienteering event software) and provides competition data for graphics displays.
meos-graphics/
├── cmd/
│ └── meos-graphics/
│ └── main.go # Application entry point
├── internal/ # Private application code
│ ├── handlers/ # HTTP request handlers
│ │ ├── handlers.go # Main handler implementations
│ │ └── types.go # Response type definitions
│ ├── logger/ # Logging functionality
│ │ └── logger.go # Logger initialization and configuration
│ ├── meos/ # MeOS integration
│ │ ├── adapter.go # MeOS adapter implementation
│ │ ├── config.go # MeOS configuration
│ │ └── types.go # MeOS XML type definitions
│ ├── middleware/ # HTTP middleware
│ │ └── logger.go # Request logging middleware
│ ├── models/ # Domain models
│ │ └── models.go # Core data structures
│ └── state/ # Application state management
│ └── state.go # Global state with thread-safe access
└── logs/ # Log files directory
- Connects to MeOS information server via XML API
- Polls for updates every second
- Thread-safe in-memory state management
- REST API endpoints for competition graphics
- Logging to both console and file
The application includes a built-in web interface for viewing competition data:
- URL:
http://localhost:8090/(redirects to/web) - Features:
- Real-time updates via Server-Sent Events (SSE)
- Responsive design with TailwindCSS
- Interactive navigation between classes
- Live connection status indicator
- No JavaScript framework required (uses HTMX)
/web- Main page listing all classes/web/classes/:classId- View specific class with tabs for:- Start List
- Results
- Split Times
Interactive API documentation is available via Swagger UI:
- URL:
http://localhost:8090/docs - OpenAPI Spec:
http://localhost:8090/swagger/swagger.json
GET /health- Health check endpointGET /classes- List all competition classesGET /classes/:classId/startlist- Get start list for a classGET /classes/:classId/results- Get results with positions and radio timesGET /classes/:classId/splits- Get split time standings at each controlGET /sse- Server-Sent Events endpoint for real-time updates
All configuration is done through command-line flags. For a complete reference of all available flags, see docs/CLI_FLAGS.md.
Common flags:
--simulation- Run in simulation mode (no MeOS server required)--meos-host <hostname>- MeOS server hostname or IP (default: localhost)--meos-port <port>- MeOS server port (default: 2009, use 'none' to omit port)--poll-interval <duration>- How often to fetch updates from MeOS (default: 1s)--version- Show version information--help- Show help for all available flags
Examples:
# Connect to local MeOS server
go run ./cmd/meos-graphics
# Connect to remote MeOS server
go run ./cmd/meos-graphics --meos-host 192.168.1.100 --meos-port 8080
# Connect without port (for reverse proxy setups)
go run ./cmd/meos-graphics --meos-host meos.example.com --meos-port none
# Use faster polling for more responsive updates
go run ./cmd/meos-graphics --poll-interval 200msThe --poll-interval flag accepts Go duration strings:
- Minimum: 100ms
- Maximum: 1 hour
- Default: 1s
Lower intervals provide more responsive updates but increase network traffic and server load.
# Pull from GitHub Container Registry
docker pull ghcr.io/metsaapp/meos-graphics:latest
# Run in normal mode
docker run -p 8090:8090 ghcr.io/metsaapp/meos-graphics:latest
# Run in simulation mode
docker run -p 8090:8090 ghcr.io/metsaapp/meos-graphics:latest --simulation
# With custom MeOS server
docker run -p 8090:8090 ghcr.io/metsaapp/meos-graphics:latest --meos-host 192.168.1.100 --meos-port 8080
# With custom poll interval (default: 1s)
docker run -p 8090:8090 ghcr.io/metsaapp/meos-graphics:latest --poll-interval 500ms
# With persistent logs
docker run -p 8090:8090 -v $(pwd)/logs:/app/logs ghcr.io/metsaapp/meos-graphics:latest# Normal mode
go run ./cmd/meos-graphics
# Simulation mode
go run ./cmd/meos-graphics --simulation
# Custom MeOS server
go run ./cmd/meos-graphics --meos-host 192.168.1.100 --meos-port 8080
# Custom poll interval
go run ./cmd/meos-graphics --poll-interval 200ms
# Show version
go run ./cmd/meos-graphics --versionThe server will start on port 8090.
The simulation mode generates test data for development and testing without requiring a MeOS server. It runs a 15-minute cycle:
- Minutes 0-3: Start list phase - all competitors registered but not started
- Minutes 3-10: Running phase - competitors progressively receive split times and finish
- Minutes 10-15: Results phase - all competitors finished, stable results
- After 15 minutes: Automatic restart with fresh data
Features:
- All competitors start at the same time
- Realistic time variations with deciseconds
- 3 classes with different numbers of radio controls
- Random clubs and names from predefined lists
- gin-gonic/gin - HTTP web framework
This project uses Semantic Versioning and Conventional Commits for automatic version management.
All commits should follow the conventional commit format:
<type>(<scope>): <subject>
<body>
<footer>
Types:
feat:- New features (triggers minor version bump)fix:- Bug fixes (triggers patch version bump)docs:- Documentation changesstyle:- Code style changes (formatting, etc.)refactor:- Code refactoringtest:- Test additions or correctionschore:- Maintenance tasksci:- CI/CD changesbuild:- Build system changes
Breaking changes:
- Add
!after type:feat!: breaking change - Or add
BREAKING CHANGE:in the commit body - These trigger major version bumps
Releases are fully automated using release-please:
- Merge PRs with conventional commits to
main - Release-please creates/updates a release PR
- When the release PR is merged:
- A new GitHub release is created
- Version numbers are bumped
- CHANGELOG.md is generated
- Binary artifacts are built for multiple platforms
- Docker images are published to ghcr.io
Docker images are automatically published to GitHub Container Registry:
ghcr.io/metsaapp/meos-graphics:latest- Latest releaseghcr.io/metsaapp/meos-graphics:vX.Y.Z- Specific version
Images are multi-platform (linux/amd64, linux/arm64).
The application follows Go best practices with clear separation of concerns:
- Models define the core domain objects
- MeOS adapter handles communication with the MeOS server
- State provides thread-safe storage and access to competition data
- Handlers implement the REST API endpoints
- Logger provides structured logging to file and console
- Middleware handles cross-cutting concerns like request logging
This project uses Lefthook for pre-commit hooks to ensure code quality before commits are made.
-
Install Lefthook:
# Using Go go install github.com/evilmartians/lefthook@latest # Or using Homebrew (macOS) brew install lefthook # Or download binary from releases # https://github.com/evilmartians/lefthook/releases
-
Install golangci-lint (required for linting):
# Binary installation (recommended) curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(go env GOPATH)/bin v1.62.2 # See https://golangci-lint.run/usage/install/#local-installation for other methods
-
Install the git hooks:
lefthook install
The pre-commit hooks run the following checks in parallel:
- Build: Ensures the code compiles (
go build) - Test: Runs all unit tests (
go test) - Lint: Runs golangci-lint with project settings
- Docs: Ensures CLI documentation is up-to-date
If you need to bypass the hooks temporarily:
# Skip all hooks
git commit --no-verify
# Skip specific hooks using tags
LEFTHOOK_EXCLUDE=lint,test git commitYou can run the hooks manually without committing:
# Run all pre-commit hooks
lefthook run pre-commit
# Run specific hooks by tag
lefthook run pre-commit --tags=build,testThis project has two types of automatically generated documentation that must be kept up-to-date:
The CLI documentation in docs/CLI_FLAGS.md is automatically generated from the code:
# Generate CLI documentation
go run ./cmd/generate-docsThe API documentation is generated using Swagger annotations in the code:
# Install swag tool (one-time setup)
go install github.com/swaggo/swag/cmd/swag@latest
# Generate Swagger documentation
swag init -g cmd/meos-graphics/main.go --parseDependency --parseInternalThe generated files are:
docs/docs.go- Go code for embedding docsdocs/swagger.json- OpenAPI specification in JSONdocs/swagger.yaml- OpenAPI specification in YAML
Important: The CI pipeline and pre-commit hooks validate that all documentation is up-to-date. If you:
- Add or modify command-line flags → regenerate CLI docs
- Add or modify API endpoints → regenerate Swagger docs
- Change API request/response types → regenerate Swagger docs
Always commit the generated documentation files along with your code changes.
This project is licensed under the MIT License - see the LICENSE file for details.