# Communication Sync Protocol — Old Implementation Reference

This document describes the USB sync protocol from the old Android (Java/GreenDAO) implementation. Use this as a reference when building the Flutter API layer so that the response format matches what the desktop companion app expects.

---

## 1. Transport Layer

| Setting       | Value                    |
|---------------|--------------------------|
| Protocol      | TCP socket               |
| Port          | **59900**                |
| Connection    | USB tethering (ADB port forwarding) |
| Encoding      | UTF-8                    |
| Connection type | Single-request per connection — socket closes after one operation |

The phone runs a **TCP server** (`UsbServer`, singleton). The desktop app connects as a client. Each connection handles exactly one command, then the socket is closed.

---

## 2. Protocol Commands

### 2.1 Export (Phone → Desktop): `#get_all#`

```
Desktop sends:   #get_all#\n
Phone responds:  <single-line JSON string>\n
Socket closes.
```

### 2.2 Import (Desktop → Phone): `#start#`

```
Desktop sends:   #start#\n
                 <action>\n            ← "#db#" or "#file#"
                 <data line 1>\n
                 <data line 2>\n
                 ...
                 #end#\n
Socket closes.
```

- **Action `#db#`**: Data is a JSON array of `Model` objects (see Section 4).
- **Action `#file#`**: Data is raw bytes written to `<externalFilesDir>/maps.kmz`.

### 2.3 Sentinel Constants

```
#get_all#    → Export request
#start#      → Import begin
#end#        → Import termination
#db#         → Database import action
#file#       → KMZ file import action
```

---

## 3. Server Events (State Machine)

```
WAITING → UPLOADING → EXPORTING → COMPLETED
WAITING → DOWNLOADING → IMPORTING → COMPLETED
Any state → ERROR
```

| Event        | Meaning                                      |
|--------------|----------------------------------------------|
| `WAITING`    | Server listening, no active connection        |
| `UPLOADING`  | Desktop requested `#get_all#` (export)        |
| `EXPORTING`  | App is serializing data to JSON               |
| `DOWNLOADING`| Desktop sent `#start#` (import)               |
| `IMPORTING`  | App is deserializing and persisting data       |
| `COMPLETED`  | Operation finished successfully                |
| `ERROR`      | Exception occurred                             |

---

## 4. Data Wrapper — `Model` Class

Every table's data is wrapped in a `Model` envelope:

```json
{
  "type": "<identifier>",
  "data": "<JSON array string>"
}
```

**Important**: The `data` field is a **JSON string** (escaped), not a nested JSON object.

### Type identifiers differ between export and import:

| Direction | `type` value       | Example            |
|-----------|--------------------|--------------------|
| **Export** (phone→desktop) | Table name         | `"TBL_ACCESSORY"`  |
| **Import** (desktop→phone) | Java class name    | `"Accessory"`      |

**Import class names** (desktop must use these exactly):
`Accessory`, `Package`, `PackageDetail`, `Place`, `PlaceDetail`, `Style`, `Transformer`

**Export table names**:
`TBL_ACCESSORY`, `TBL_PACKAGE`, `TBL_PACKAGE_DETAIL`, `TBL_TRANSFORMER`, `TBL_PLACE`, `TBL_PLACE_DETAIL`

> Note: `Style` is **not exported** — only the 6 tables above are included in the export payload.

---

## 5. Export Payload Structure

The phone responds with a JSON array of `Model` objects. The tables are exported in this fixed order:

```json
[
  { "type": "TBL_ACCESSORY",      "data": "[...]" },
  { "type": "TBL_PACKAGE",        "data": "[...]" },
  { "type": "TBL_PACKAGE_DETAIL", "data": "[...]" },
  { "type": "TBL_TRANSFORMER",    "data": "[...]" },
  { "type": "TBL_PLACE",          "data": "[...]" },
  { "type": "TBL_PLACE_DETAIL",   "data": "[...]" }
]
```

### Export rules:
- **Accessory, Package, PackageDetail, Transformer**: Export **all** rows (`loadAll()`)
- **Place**: Export only rows where `is_collected == true`
- **PlaceDetail**: Export only rows belonging to collected places
- **Style**: **Not exported**

### Gson serialization config:
- Date format: `"yyyy-MM-dd HH:mm:ss"`
- Exclusion: Fields of type `PackageDetail.class`, `Accessory.class`, `Style.class`, `List.class` are **skipped** (prevents serializing GreenDAO relation objects / circular references)

---

## 6. Import Process

For each `Model` entry in the received JSON array:

1. **Delete all** existing rows in the target table
2. Deserialize the JSON array string into entity objects
3. **Insert** each entity row by row

```
For each Model in array:
  → DELETE ALL from table
  → PARSE data JSON array
  → INSERT each row
```

### Import order matters (foreign key integrity):

Recommended import order from desktop:
1. `Style` (referenced by Place)
2. `Accessory` (referenced by PlaceDetail, PackageDetail)
3. `Package` (referenced by PackageDetail)
4. `PackageDetail` (depends on Package + Accessory)
5. `Place` (depends on Style)
6. `PlaceDetail` (depends on Place + Accessory)
7. `Transformer` (depends on Place)

### No transaction wrapping:
Each insert is individual. A failure mid-import leaves the database in a partially cleared state (data already deleted, only some rows re-inserted).

### No acknowledgment:
The phone does not send a response back to the desktop for import operations. The socket simply closes.

---

## 7. Entity JSON Schemas

### 7.1 Accessory

```json
{
  "id": 1,
  "name": "ដែក Beam 6m",
  "note": "",
  "is_active": true,
  "is_new": false
}
```

| Field     | Type    | Required | Notes |
|-----------|---------|----------|-------|
| id        | Long    | Yes      | Primary key |
| name      | String  | Yes      | Display name |
| note      | String  | No       | Optional description |
| is_active | boolean | Yes      | Active/inactive flag |
| is_new    | boolean | Yes      | Created on phone (not synced from desktop) |

### 7.2 Style

```json
{
  "id": 1,
  "name": "12m Pole",
  "note": "Standard 12 meter pole"
}
```

| Field | Type   | Required | Notes |
|-------|--------|----------|-------|
| id    | Long   | Yes      | Primary key |
| name  | String | Yes      | Display name |
| note  | String | No       | Optional description |

### 7.3 Package

```json
{
  "id": 1,
  "name": "បង្គោល ១២ មេត",
  "is_active": true,
  "is_new": false
}
```

| Field     | Type    | Required | Notes |
|-----------|---------|----------|-------|
| id        | Long    | Yes      | Primary key |
| name      | String  | Yes      | Display name |
| is_active | boolean | Yes      | Active/inactive flag |
| is_new    | boolean | Yes      | Created on phone |

### 7.4 PackageDetail (Package ↔ Accessory junction)

```json
{
  "id": 1,
  "package_id": 1,
  "accessory_id": 2,
  "qty": 40.0,
  "is_active": true
}
```

| Field        | Type    | Required | Notes |
|--------------|---------|----------|-------|
| id           | Long    | Yes      | Primary key |
| package_id   | long    | Yes      | FK → Package.id |
| accessory_id | long    | Yes      | FK → Accessory.id |
| qty          | float   | Yes      | Quantity of this accessory in the package |
| is_active    | boolean | Yes      | Soft-delete flag |

### 7.5 Place

```json
{
  "id": 724,
  "ref_id": 1,
  "object_id": 5,
  "style_id": 5,
  "area": "A01",
  "pole": "A001",
  "latitude": 11.78143787,
  "longitude": 105.01094055,
  "is_own": true,
  "collected_date": "2017-08-01 10:30:00",
  "is_collected": true
}
```

| Field          | Type    | Required | Notes |
|----------------|---------|----------|-------|
| id             | Long    | Yes      | Primary key |
| ref_id         | int     | Yes      | Reference type: `1` = Pole, `2` = Transformer |
| object_id      | long    | Yes      | ID of the referenced object |
| style_id       | long    | Yes      | FK → Style.id |
| area           | String  | No       | Area/zone code |
| pole           | String  | Yes      | Pole identifier (unique per area) |
| latitude       | float   | Yes      | GPS latitude |
| longitude      | float   | Yes      | GPS longitude |
| is_own         | boolean | Yes      | Owned by EPower |
| collected_date | Date    | No       | Format: `"yyyy-MM-dd HH:mm:ss"` |
| is_collected   | boolean | Yes      | `true` = data collected, included in export |

### 7.6 PlaceDetail (Place ↔ Accessory junction)

```json
{
  "id": 1,
  "place_id": 724,
  "accessory_id": 2,
  "qty": 3.0,
  "is_active": true
}
```

| Field        | Type    | Required | Notes |
|--------------|---------|----------|-------|
| id           | Long    | Yes      | Primary key |
| place_id     | long    | Yes      | FK → Place.id |
| accessory_id | long    | Yes      | FK → Accessory.id |
| qty          | float   | Yes      | Quantity of this accessory at this place |
| is_active    | boolean | Yes      | Soft-delete flag |

### 7.7 Transformer

```json
{
  "id": 1,
  "place_id": 5,
  "name": "Transformer A",
  "note": "50kVA",
  "is_active": true,
  "is_new": false
}
```

| Field     | Type    | Required | Notes |
|-----------|---------|----------|-------|
| id        | Long    | Yes      | Primary key |
| place_id  | long    | Yes      | FK → Place.id |
| name      | String  | Yes      | Display name |
| note      | String  | No       | Optional description |
| is_active | boolean | Yes      | Active/inactive flag |
| is_new    | boolean | Yes      | Created on phone |

---

## 8. Entity Relationship Diagram

```
┌─────────┐           ┌─────────────┐           ┌────────────┐
│  Style  │ 1 ◄────── N │    Place    │ 1 ──────► N │PlaceDetail │
│         │           │             │           │            │
│ id      │           │ id          │           │ id         │
│ name    │           │ ref_id      │           │ place_id   │──► Place.id
│ note    │           │ object_id   │           │accessory_id│──► Accessory.id
└─────────┘           │ style_id ───┘           │ qty        │
                      │ area        │           │ is_active  │
                      │ pole        │           └────────────┘
                      │ latitude    │                  │
                      │ longitude   │                  ▼
                      │ is_own      │           ┌────────────┐
                      │collected_date│          │ Accessory  │
                      │ is_collected│           │            │
                      └─────────────┘           │ id         │
                             ▲                  │ name       │
                             │                  │ note       │
                      ┌──────┘                  │ is_active  │
                      │                         │ is_new     │
                ┌─────────────┐                 └────────────┘
                │ Transformer │                        ▲
                │             │                        │
                │ id          │           ┌────────────┘
                │ place_id ───┘           │
                │ name        │    ┌──────────────┐
                │ note        │    │PackageDetail  │
                │ is_active   │    │              │
                │ is_new      │    │ id           │
                └─────────────┘    │ package_id   │──► Package.id
                                   │ accessory_id │──► Accessory.id
                                   │ qty          │
                ┌─────────┐       │ is_active    │
                │ Package │ 1 ──► N └──────────────┘
                │         │
                │ id      │
                │ name    │
                │ is_active│
                │ is_new  │
                └─────────┘
```

---

## 9. Cascade Operations

### Place save (collect):
1. Set `collected_date` = now, `is_collected` = true
2. Update the Place row
3. For each existing PlaceDetail not in the new list → set `is_active = false` (soft delete)
4. For each new PlaceDetail (id == null) → insert with `place_id` set
5. For each existing PlaceDetail in the new list → update

### Place delete:
1. Delete all PlaceDetails for this place (hard delete)
2. Delete the Place

### Package save:
1. Insert/update the Package
2. Soft-delete removed PackageDetails (`is_active = false`)
3. Insert new PackageDetails / update existing ones

### Package delete:
1. Delete all PackageDetails for this package (hard delete)
2. Delete the Package

---

## 10. Key Implementation Notes for Flutter API

1. **Date format** must be `"yyyy-MM-dd HH:mm:ss"` — used by Gson on both import and export.

2. **Export filters**: Only export places where `is_collected == true` and their associated PlaceDetails. All other tables export all rows.

3. **The `data` field is a string**: When building the API response, the `data` value inside each `Model` must be a JSON **string** (i.e., the array is serialized to a string, not embedded as a raw JSON array). Example:
   ```json
   {"type": "TBL_ACCESSORY", "data": "[{\"id\":1, ...}]"}
   ```
   Not:
   ```json
   {"type": "TBL_ACCESSORY", "data": [{"id": 1, ...}]}
   ```

4. **Import is destructive**: Each table is fully cleared before re-inserting. No merge/upsert logic.

5. **No response on import**: The server does not acknowledge the import. The desktop app just sends data and closes.

6. **Relation fields are excluded from serialization**: When exporting, GreenDAO relation objects (`@ToOne`, `@ToMany`) are excluded. Only the foreign key columns (e.g., `style_id`, `place_id`, `accessory_id`) are serialized — not the resolved entity objects.

7. **Float fields**: `qty`, `latitude`, `longitude` are floats (not doubles). Ensure JSON parsing handles this correctly.

8. **Soft delete pattern**: `is_active = false` is used for soft deletion in PlaceDetail and PackageDetail. Hard delete is used when removing the parent entity.

9. **`is_new` flag**: Indicates the record was created on the phone (not imported from desktop). Useful for the desktop app to know which records are new.

10. **Style is not exported**: The export payload does not include a `TBL_STYLE` entry. Styles are reference data managed only on the desktop side.

---

## 11. API Endpoint Design (Flutter Replacement)

When building the REST/HTTP API to replace USB sync, the JSON format should match the old `Model[]` structure so the desktop app requires minimal changes.

### 11.1 Export Endpoint

```
GET /api/sync/export
Authorization: Bearer <token>
```

**Response** (`200 OK`):

```json
[
  { "type": "TBL_ACCESSORY",      "data": "[{\"id\":1, ...}]" },
  { "type": "TBL_PACKAGE",        "data": "[{\"id\":1, ...}]" },
  { "type": "TBL_PACKAGE_DETAIL", "data": "[{\"id\":1, ...}]" },
  { "type": "TBL_TRANSFORMER",    "data": "[{\"id\":1, ...}]" },
  { "type": "TBL_PLACE",          "data": "[{\"id\":1, ...}]" },
  { "type": "TBL_PLACE_DETAIL",   "data": "[{\"id\":1, ...}]" }
]
```

Same rules as Section 5 — only collected places, no styles, FK-only (no nested relations).

### 11.2 Import Endpoint

```
POST /api/sync/import
Authorization: Bearer <token>
Content-Type: application/json
```

**Request body**:

```json
[
  { "type": "Style",         "data": "[...]" },
  { "type": "Accessory",     "data": "[...]" },
  { "type": "Package",       "data": "[...]" },
  { "type": "PackageDetail", "data": "[...]" },
  { "type": "Place",         "data": "[...]" },
  { "type": "PlaceDetail",   "data": "[...]" },
  { "type": "Transformer",   "data": "[...]" }
]
```

**Response** (`200 OK`):

```json
{
  "success": true,
  "tables_imported": ["Style", "Accessory", "Package", "PackageDetail", "Place", "PlaceDetail", "Transformer"],
  "row_counts": { "Style": 5, "Accessory": 20, "Package": 3, "PackageDetail": 12, "Place": 50, "PlaceDetail": 120, "Transformer": 8 }
}
```

**Error Response** (`400` / `500`):

```json
{
  "success": false,
  "error": "Failed to import PackageDetail: invalid accessory_id reference",
  "tables_completed": ["Style", "Accessory", "Package"],
  "table_failed": "PackageDetail"
}
```

### 11.3 File Import (KMZ Maps)

```
POST /api/sync/import-map
Authorization: Bearer <token>
Content-Type: application/octet-stream
```

**Body**: Raw KMZ file bytes → saved to device storage as `maps.kmz`.

### 11.4 Improvements Over Old Protocol

| Aspect              | Old (USB/TCP)                  | New (API)                        |
|---------------------|--------------------------------|----------------------------------|
| Transport           | TCP socket port 59900          | HTTP/HTTPS                       |
| Connection          | ADB port forwarding required   | WiFi / LAN / Internet            |
| Authentication      | None                           | Bearer token                     |
| Error handling      | Silent failure                 | HTTP status codes + error body   |
| Acknowledgment      | None on import                 | JSON response with row counts    |
| Transaction safety  | No rollback on failure         | Wrap in DB transaction           |
| Type naming         | Asymmetric (table vs class)    | Standardize on one convention    |
