Add doc/displays-signals.md
This commit is contained in:
parent
7308a6f9bf
commit
e59abf1d87
439
doc/displays-signals.md
Normal file
439
doc/displays-signals.md
Normal file
@ -0,0 +1,439 @@
|
||||
# 📡 Taekwondo Scoring System – Score Control to Display Communication
|
||||
|
||||
> **Version:** 1.0
|
||||
> **Last Updated:** July 2025
|
||||
> **Target Developer:** .NET Android (Score Control) & Android TV App Developers
|
||||
> **Purpose:** Define **real-time data signals** from **Score Control Unit** to **Scoreboard** and **Upcoming Matches** displays
|
||||
|
||||
---
|
||||
|
||||
## 📌 Table of Contents
|
||||
|
||||
- [1. Overview](#1-overview)
|
||||
- [2. System Architecture](#2-system-architecture)
|
||||
- [3. Communication Protocol (TCP)](#3-communication-protocol-tcp)
|
||||
- [4. Scoreboard (Live Match Display)](#4-scoreboard-live-match-display)
|
||||
- [5. Upcoming Matches Screen](#5-upcoming-matches-screen)
|
||||
- [6. Message Format (JSON)](#6-message-format-json)
|
||||
- [7. C# Server Code (Score Control)](#7-c-server-code-score-control)
|
||||
- [8. Android TV App Logic](#8-android-tv-app-logic)
|
||||
- [9. Display Modes & Transitions](#9-display-modes--transitions)
|
||||
- [10. Best Practices](#10-best-practices)
|
||||
|
||||
---
|
||||
|
||||
## 1. Overview
|
||||
|
||||
The **Score Control Unit (Android tablet)** sends **real-time data** to **two Android TVs**:
|
||||
|
||||
| Display | Purpose |
|
||||
|--------|--------|
|
||||
| **Scoreboard TV** | Live match: score, timer, fighter info |
|
||||
| **Upcoming Matches TV** | Lobby: next matches, tournament flow |
|
||||
|
||||
All communication is:
|
||||
- ✅ **Real-time**
|
||||
- ✅ **Bidirectional-ready** (optional)
|
||||
- ✅ **Over Wi-Fi (TCP)**
|
||||
- ✅ **Zero-config for users**
|
||||
|
||||
---
|
||||
|
||||
## 2. System Architecture
|
||||
|
||||
```
|
||||
[Score Control Tablet] → Wi-Fi → [Scoreboard TV (Port 5001)]
|
||||
↘
|
||||
[Upcoming Matches TV (Port 5002)]
|
||||
```
|
||||
|
||||
- Score Control acts as **TCP server**
|
||||
- TVs act as **TCP clients**
|
||||
- TVs connect at startup and **stay connected**
|
||||
- Score Control **broadcasts** JSON messages
|
||||
|
||||
> ✅ All devices on same network (`TKD_SCORING_COURT1`)
|
||||
|
||||
---
|
||||
|
||||
## 3. Communication Protocol (TCP)
|
||||
|
||||
### 🔌 Connection Flow
|
||||
1. TV powers on → connects to `TKD_SCORING_COURT1`
|
||||
2. TV app starts → connects to tablet IP (`192.168.4.1`)
|
||||
3. TV sends identity:
|
||||
```
|
||||
DISPLAY:NAME=TV_SCOREBOARD,TYPE=SCOREBOARD,MAC=18:3D:A2:01:02:03
|
||||
```
|
||||
4. Score Control registers TV
|
||||
5. TV listens for JSON messages
|
||||
|
||||
---
|
||||
|
||||
## 4. Scoreboard (Live Match Display)
|
||||
|
||||
Displays real-time match data.
|
||||
|
||||
### 🖼️ 13 Display Elements
|
||||
| Element | Source |
|
||||
|-------|--------|
|
||||
| Court Number | Match data |
|
||||
| Match Number | Schedule |
|
||||
| Match Timer | App timer |
|
||||
| Round Counter | Match state |
|
||||
| Country Flag | Fighter data |
|
||||
| Country ISO3 | Fighter data |
|
||||
| Team Logo | Fighter data |
|
||||
| Fighter Name | Match setup |
|
||||
| Fighter Score | Scoring engine |
|
||||
| Fighter Fouls | Penalty tracker |
|
||||
| Fighter Weight (kg) | Fighter profile |
|
||||
| Weight Class & Category | Match data |
|
||||
| Team Name | Fighter data |
|
||||
|
||||
---
|
||||
|
||||
### 🔄 Display Modes
|
||||
| Mode | Trigger | Duration |
|
||||
|------|--------|---------|
|
||||
| `faceoff` | Before match | 10 sec |
|
||||
| `score` | Match start | 2:00 |
|
||||
| `break` | Round end | 60 sec |
|
||||
| `call_to_action` | Last 10 sec of break | 10 sec |
|
||||
| `winner` | Match end | 15 sec |
|
||||
|
||||
---
|
||||
|
||||
## 5. Upcoming Matches Screen
|
||||
|
||||
Displays next 3–5 matches.
|
||||
|
||||
### 🖼️ Display Elements
|
||||
- Match number
|
||||
- Red vs Blue names
|
||||
- Weight class
|
||||
- Round
|
||||
- Scheduled time
|
||||
- Tournament logo
|
||||
|
||||
### 🔄 Auto-Advance
|
||||
- Updates when current match ends
|
||||
- Pulls from CSV or cloud
|
||||
- Scrolls if more than 5 matches
|
||||
|
||||
---
|
||||
|
||||
## 6. Message Format (JSON)
|
||||
|
||||
All messages are **JSON** and end with `\n`.
|
||||
|
||||
---
|
||||
|
||||
### 6.1 To Scoreboard TV (Port 5001)
|
||||
|
||||
#### `faceoff` Mode
|
||||
```json
|
||||
{
|
||||
"mode": "faceoff",
|
||||
"red": {
|
||||
"name": "Kim Min-jae",
|
||||
"country": "KOR",
|
||||
"flag": "https://flags/kor.png",
|
||||
"photo": "https://photos/kim.jpg"
|
||||
},
|
||||
"blue": {
|
||||
"name": "Lee Jung-ho",
|
||||
"country": "CHN",
|
||||
"flag": "https://flags/chn.png",
|
||||
"photo": "https://photos/lee.jpg"
|
||||
},
|
||||
"weight_class": "Men's -68kg",
|
||||
"round": "Semifinal"
|
||||
}
|
||||
```
|
||||
|
||||
#### `score` Mode
|
||||
```json
|
||||
{
|
||||
"mode": "score",
|
||||
"court": "1",
|
||||
"match_number": "105",
|
||||
"weight_class": "Men's -68kg",
|
||||
"category": "Semifinal",
|
||||
"round": "2",
|
||||
"timer": "01:45",
|
||||
"red": {
|
||||
"name": "Kim Min-jae",
|
||||
"country": "KOR",
|
||||
"team": "Seoul TKD Club",
|
||||
"logo": "https://logos/seoul.png",
|
||||
"flag": "https://flags/kor.png",
|
||||
"score": 8,
|
||||
"fouls": 1,
|
||||
"weight_kg": 67.2
|
||||
},
|
||||
"blue": {
|
||||
"name": "Lee Jung-ho",
|
||||
"country": "CHN",
|
||||
"team": "Beijing TKD",
|
||||
"logo": "https://logos/beijing.png",
|
||||
"flag": "https://flags/chn.png",
|
||||
"score": 5,
|
||||
"fouls": 2,
|
||||
"weight_kg": 67.8
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### `break` Mode
|
||||
```json
|
||||
{
|
||||
"mode": "break",
|
||||
"ad_image": "https://ads/sponsor1.png",
|
||||
"highlight_video": "https://videos/round1.mp4",
|
||||
"countdown": true
|
||||
}
|
||||
```
|
||||
|
||||
#### `call_to_action` Mode
|
||||
```json
|
||||
{
|
||||
"mode": "call_to_action",
|
||||
"message": "FIGHTERS, RETURN TO THE RING!",
|
||||
"timer": 10
|
||||
}
|
||||
```
|
||||
|
||||
#### `winner` Mode
|
||||
```json
|
||||
{
|
||||
"mode": "winner",
|
||||
"winner": "red",
|
||||
"red": {
|
||||
"name": "Kim Min-jae",
|
||||
"photo": "https://photos/kim.jpg",
|
||||
"final_score": 8
|
||||
},
|
||||
"blue": {
|
||||
"name": "Lee Jung-ho",
|
||||
"photo": "https://photos/lee.jpg",
|
||||
"final_score": 5
|
||||
},
|
||||
"weight_class": "Men's -68kg",
|
||||
"round": "Final",
|
||||
"animation": "fireworks"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 6.2 To Upcoming Matches TV (Port 5002)
|
||||
|
||||
```json
|
||||
{
|
||||
"mode": "next_matches",
|
||||
"matches": [
|
||||
{
|
||||
"match_number": "106",
|
||||
"red": "Park S.",
|
||||
"blue": "Wang L.",
|
||||
"weight": "+87kg",
|
||||
"round": "Final",
|
||||
"time": "11:30",
|
||||
"category": "Men's"
|
||||
},
|
||||
{
|
||||
"match_number": "107",
|
||||
"red": "Choi M.",
|
||||
"blue": "Tanaka Y.",
|
||||
"weight": "-58kg",
|
||||
"round": "Semifinal",
|
||||
"time": "11:50",
|
||||
"category": "Women's"
|
||||
}
|
||||
],
|
||||
"tournament_name": "National Taekwondo Championship 2025",
|
||||
"logo": "https://logos/tournament.png"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 7. C# Server Code (Score Control)
|
||||
|
||||
```csharp
|
||||
public class DisplayServer
|
||||
{
|
||||
private TcpListener _scoreboardServer;
|
||||
private TcpListener _matchesServer;
|
||||
private List<TcpClient> _scoreboardClients = new();
|
||||
private List<TcpClient> _matchesClients = new();
|
||||
|
||||
public async Task Start()
|
||||
{
|
||||
_scoreboardServer = new TcpListener(IPAddress.Any, 5001);
|
||||
_matchesServer = new TcpListener(IPAddress.Any, 5002);
|
||||
|
||||
_scoreboardServer.Start();
|
||||
_matchesServer.Start();
|
||||
|
||||
_ = AcceptScoreboardClients();
|
||||
_ = AcceptMatchesClients();
|
||||
}
|
||||
|
||||
private async Task AcceptScoreboardClients()
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
var client = await _scoreboardServer.AcceptTcpClientAsync();
|
||||
_scoreboardClients.Add(client);
|
||||
_ = HandleDisplayClient(client, "SCOREBOARD");
|
||||
}
|
||||
}
|
||||
|
||||
private async Task AcceptMatchesClients()
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
var client = await _matchesServer.AcceptTcpClientAsync();
|
||||
_matchesClients.Add(client);
|
||||
_ = HandleDisplayClient(client, "MATCHES");
|
||||
}
|
||||
}
|
||||
|
||||
private async Task HandleDisplayClient(TcpClient client, string type)
|
||||
{
|
||||
using var stream = client.GetStream();
|
||||
using var reader = new StreamReader(stream);
|
||||
|
||||
string identity = await reader.ReadLineAsync();
|
||||
// Validate: DISPLAY:NAME=...,TYPE=...,MAC=...
|
||||
|
||||
// Keep connection open
|
||||
while (client.Connected)
|
||||
{
|
||||
await Task.Delay(1000); // Wait for broadcast
|
||||
}
|
||||
|
||||
// Remove on disconnect
|
||||
if (type == "SCOREBOARD")
|
||||
_scoreboardClients.Remove(client);
|
||||
else
|
||||
_matchesClients.Remove(client);
|
||||
}
|
||||
|
||||
public void BroadcastToScoreboard(object data)
|
||||
{
|
||||
string json = JsonSerializer.Serialize(data) + "\n";
|
||||
byte[] bytes = Encoding.UTF8.GetBytes(json);
|
||||
|
||||
for (int i = _scoreboardClients.Count - 1; i >= 0; i--)
|
||||
{
|
||||
var client = _scoreboardClients[i];
|
||||
if (client?.Connected == true)
|
||||
{
|
||||
try
|
||||
{
|
||||
client.GetStream().Write(bytes, 0, bytes.Length);
|
||||
}
|
||||
catch
|
||||
{
|
||||
_scoreboardClients.RemoveAt(i); // Cleanup
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void BroadcastToMatches(object data)
|
||||
{
|
||||
string json = JsonSerializer.Serialize(data) + "\n";
|
||||
byte[] bytes = Encoding.UTF8.GetBytes(json);
|
||||
|
||||
for (int i = _matchesClients.Count - 1; i >= 0; i--)
|
||||
{
|
||||
var client = _matchesClients[i];
|
||||
if (client?.Connected == true)
|
||||
{
|
||||
try
|
||||
{
|
||||
client.GetStream().Write(bytes, 0, bytes.Length);
|
||||
}
|
||||
catch
|
||||
{
|
||||
_matchesClients.RemoveAt(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 8. Android TV App Logic
|
||||
|
||||
### 📺 Scoreboard TV App
|
||||
- Connects to `192.168.4.1:5001`
|
||||
- Sends identity
|
||||
- Listens for JSON
|
||||
- Renders based on `mode`
|
||||
- Supports video playback for `highlight_video`
|
||||
|
||||
### 📺 Upcoming Matches TV App
|
||||
- Connects to `192.168.4.1:5002`
|
||||
- Displays scrollable list
|
||||
- Updates on new message
|
||||
- Shows tournament logo
|
||||
|
||||
---
|
||||
|
||||
## 9. Display Modes & Transitions
|
||||
|
||||
| Action | Broadcast To | Mode |
|
||||
|-------|--------------|------|
|
||||
| Match selected | Scoreboard | `faceoff` |
|
||||
| "Shi-jak" pressed | Scoreboard | `score` |
|
||||
| Round ends | Scoreboard | `break` |
|
||||
| 10 sec before next round | Scoreboard | `call_to_action` |
|
||||
| Match ends | Scoreboard | `winner` |
|
||||
| Match ends | Upcoming Matches | `next_matches` |
|
||||
| Tournament starts | Upcoming Matches | `next_matches` |
|
||||
|
||||
---
|
||||
|
||||
## 10. Best Practices
|
||||
|
||||
| Practice | Why |
|
||||
|--------|-----|
|
||||
| Use separate ports (5001, 5002) | Avoid mode conflicts |
|
||||
| Send `\n` after each message | Easy parsing |
|
||||
| Validate device identity | Security |
|
||||
| Reconnect logic on TV | Handle Wi-Fi drops |
|
||||
| Broadcast only on change | Reduce network load |
|
||||
| Use UTC time in logs | Debugging |
|
||||
| Include `mode` in every message | Safe rendering |
|
||||
|
||||
---
|
||||
|
||||
🎯 **This document fully defines the communication between Score Control and Displays.**
|
||||
You now have everything to build **real-time, synchronized, broadcast-quality displays**.
|
||||
📬 For help: Contact project lead.
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### ✅ How to Use
|
||||
|
||||
1. Save as `DISPLAY_COMMUNICATION.md`
|
||||
2. Place in `/docs` folder
|
||||
3. Commit to GitHub
|
||||
|
||||
---
|
||||
|
||||
Let me know if you want:
|
||||
- A **Figma mockup** of the TV screens
|
||||
- A **sample Android TV app** (Kotlin)
|
||||
- A **test tool** to simulate messages
|
||||
- A **Lottie animation** for the winner screen
|
||||
|
||||
You're now building a **fully integrated, professional tournament system**. 🏆
|
||||
Loading…
x
Reference in New Issue
Block a user