# Geçmiş veri aktarımı

> Bir cihazın eski ölçümlerini HTTP ile küçük paketler halinde geçmiş veri deposuna gönderin. Her paket güvenle yeniden gönderilebilir.

Kaynak: https://proxus.io/tr/docs/develop/historical-import

Bu kılavuzu tamamladığınızda programınız, bir cihazın geçmişte kaydettiği ölçümleri Proxus geçmiş veri deposuna
gönderir. Bu ölçümler bir veri kaydediciden, bir CSV dosyasından ya da makinenin kendi belleğinden gelebilir.
Aktarılan değerler cihazın canlı değerleriyle aynı yerde saklanır. Trendlerde, raporlarda ve
[Telemetry API](https://proxus.io/tr/docs/develop/telemetry-api) sonuçlarında görünür.

## Başlamadan önce

Şunlar gereklidir:

- Proxus'ta tanımlı, etkin ve çalışan bir Edge Gateway'e bağlı bir cihaz. Cihazın **ID** değerini
  ([REST API](https://proxus.io/tr/docs/develop/rest-api)'deki `Oid`) ve **Erişim anahtarı** değerini (cihaz sayfasında görünür) not
  edin.
- Edge Gateway'in HTTP portuna ağ erişimi. Varsayılan port `8083`'tür (`[Service]` bölümünde `Server-Port`).
- Geçmiş veri aktarımının açık olması. Ürünle gelen yapılandırmada bu özellik kapalıdır. Nasıl açılacağı sonraki
  bölümde anlatılır.

## Geçmiş veri aktarımını açın

Aşağıdaki anahtarları ayarlayın, ardından merkez sunucuyu ve Edge Gateway'leri yeniden başlatın:

| Nerede | Anahtar | Değer |
| --- | --- | --- |
| Merkez sunucu ve aktarım verisi alacak tüm Edge Gateway'ler | `HistoricalImport__Enabled` | `true` |
| Proxus geçmiş veri deposuna yazan Edge Gateway (standart kurulumda merkez sunucudaki **SYSTEM** Edge Gateway'i) | `HistoricalImport__StorageEnabled` | `true` |

Aktarım kapalıyken uç noktalar `503` döndürür.

## Adım 1: Paket gönderin

```text
POST http://<gateway>:8083/api/v1/devices/<device ID>/historical/batches
X-IoT-Token: <device token>
Idempotency-Key: lys-packer-04-2026-10-01-0001
Content-Type: application/json
```

```json
{
  "records": [
    { "key": "Temperature", "time": "2026-10-01T06:00:00.000Z", "type": "Float", "value": 24.6, "unit": "°C" },
    { "key": "Speed",       "time": "2026-10-01T06:00:00.000Z", "type": "UShort", "value": 82, "unit": "rpm" },
    { "key": "Running",     "time": "2026-10-01T06:00:00.000Z", "type": "Bool", "value": true }
  ]
}
```

| Alan | Kurallar |
| --- | --- |
| `records` | En az 1, en çok 50 kayıt. |
| `key` | Veri noktasının adı. En çok 256 karakter olabilir. |
| `time` | ISO 8601 biçiminde, `Z` ya da `+02:00` gibi bir saat farkıyla yazılır. En çok milisaniye hassasiyetinde olabilir. Gelecekteki bir zaman veya 1900'den önceki bir tarih kabul edilmez. |
| `type` | Şu değerlerden biri olmalıdır: `Bool`, `Short`, `UShort`, `Int`, `UInt`, `Float`, `Long`, `ULong`, `Double`, `String`, `Bytes`, `DateTime`. Büyük ve küçük harf ayrımı vardır. |
| `value` | `type` ile uyumlu bir JSON değeri: `Bool` için `true`/`false`, sayısal türler için sayı, `String` için metin (en çok 4.096 karakter), `Bytes` için Base64 metin, `DateTime` için ISO 8601 metin. `Long` ve `ULong` değerleri ±9007199254740991 aralığında olmalıdır. |
| `unit` | İsteğe bağlıdır. En çok 64 karakter olabilir. |

Gövdede bunların dışında bir alan varsa istek reddedilir. Gövdenin toplam boyutu en çok 256 KiB olabilir.

**Idempotency-Key** başlığı paketin kimliğidir. 8 ile 128 karakter arasında olmalı ve yalnızca harf, rakam, `-`, `_` veya `.`
içermelidir. Aynı anahtarla aynı gövdeyi yeniden göndermek güvenlidir: API ilk yanıtı tekrar döndürür. Aynı anahtarla
farklı bir gövde gönderilirse istek reddedilir. Her paket için ayrı bir anahtar kullanın. Kaynak dosyanın adı ile
paket numarasını birleştirmek iyi bir yöntemdir.

## Adım 2: Yanıtı okuyun

Yeni bir paket `202 Accepted`, daha önce gönderilmiş bir paket `200` yanıtı alır. `Location` başlığı paketin
adresini verir.

```json
{
  "jobId": "5b0e6a5c-6f4e-4a0e-9a59-3f8f0f8c2d11",
  "state": "Pending",
  "results": [
    { "index": 0, "state": "Pending" },
    { "index": 1, "state": "Pending" },
    { "index": 2, "state": "Rejected", "error": "InvalidTypeOrValue" }
  ],
  "storageDataset": "DeviceRawData"
}
```

Her kaydın sonucu ayrı olarak, gönderim sırasıyla döner. Reddedilen kayıtlar saklanmaz, diğer kayıtlar saklanır.

| Kayıt hatası | Nedeni |
| --- | --- |
| `InvalidRecord` | `key` eksik veya çok uzun, `unit` çok uzun ya da `value` eksik. |
| `InvalidTimestamp` | `time` eksik, biçimi yanlış, gelecekte, 1900'den önce ya da milisaniyeden daha hassas. |
| `InvalidTypeOrValue` | `type` tanınmıyor ya da `value` bu türe uymuyor. |
| `MeasurementConflict` | Pakette aynı veri noktası ve zaman için iki farklı değer var. Veri noktası, zaman ve değer tamamen aynıysa kayıt bunun yerine `Duplicate` olarak işaretlenir. |

## Adım 3: Paketi izleyin

```text
GET http://<gateway>:8083/api/v1/devices/<device ID>/historical/batches/<jobId>
X-IoT-Token: <device token>
```

| Paket `state` değeri | Anlamı |
| --- | --- |
| `Pending` | Paket kabul edildi, kayıtlar yazılıyor. |
| `Completed` | Kabul edilen tüm kayıtlar saklandı. |
| `CompletedWithErrors` | İşlem bitti, ancak en az bir kayıt reddedildi. |
| `NeedsReconciliation` | Geçmiş veri deposu bazı kayıtların yazıldığını onaylamadı. Bu kayıtların sonucu `Uncertain` olur. Aynı paketi daha sonra aynı anahtarla yeniden gönderin. |

Yazma işleminden sonra kayıtların alabileceği durumlar şunlardır: `Stored`, `Duplicate` (o veri noktası ve zaman için
aynı değer zaten saklı), `Rejected` veya `Uncertain`.

## Paket bilgisi içermeyen yanıtlar

| Durum | Gövde veya başlık | Anlamı |
| --- | --- | --- |
| `400` | `{"error":"InvalidIdempotencyKey"}` | `Idempotency-Key` başlığı eksik ya da izin verilen biçimde değil. |
| `400` | `{"error":"InvalidJson"}` veya `{"error":"InvalidBatch"}` | Gövde geçerli JSON değil, tanınmayan alanlar içeriyor, hiç kayıt içermiyor ya da 50'den fazla kayıt içeriyor. |
| `401` | | `X-IoT-Token` eksik, erişim anahtarı bu cihaz ID'sine ait değil ya da cihaz devre dışı. |
| `404` | | Bu cihazda bu ID'ye sahip bir paket yok. |
| `409` | `{"error":"IdempotencyConflict"}` | Bu anahtar daha önce farklı bir gövdeyle kullanılmış. |
| `413` | | Gövde 256 KiB'tan büyük. |
| `415` | | `Content-Type` JSON değil. |
| `429` | `Retry-After: 5` | Edge Gateway meşgul (aynı anda en çok 8 istek işlenir) ya da yazılmayı bekleyen paket sayısı sınırda (cihaz başına 8, toplam 64). Biraz bekleyip aynı anahtarla yeniden gönderin. |
| `503` | | Aktarım kapalı ya da merkez sunucuya ulaşılamıyor. Daha sonra aynı anahtarla yeniden gönderin. |

## Python örneği

```python
import time
import requests

GATEWAY = "http://localhost:8083"
DEVICE_ID = 69
DEVICE_TOKEN = "<the device's token>"

def send_batch(key, records):
    while True:
        response = requests.post(
            f"{GATEWAY}/api/v1/devices/{DEVICE_ID}/historical/batches",
            json={"records": records},
            headers={"X-IoT-Token": DEVICE_TOKEN, "Idempotency-Key": key},
            timeout=30,
        )
        if response.status_code in (429, 503):
            time.sleep(int(response.headers.get("Retry-After", "5")))
            continue
        response.raise_for_status()
        return response.json()

batch = [
    {"key": "Temperature", "time": "2026-10-01T06:00:00.000Z", "type": "Float", "value": 24.6, "unit": "°C"},
    {"key": "Temperature", "time": "2026-10-01T06:01:00.000Z", "type": "Float", "value": 24.9, "unit": "°C"},
]
job = send_batch("lys-packer-04-2026-10-01-0001", batch)
print(job["state"], [r["state"] for r in job["results"]])
```

> **Not:**
Aktarılan değerler, cihazın şu anki adıyla saklanır. Bunlar canlı veri değil, geçmiş kayıtlar olduğu için işlem
akışlarından, alarm kurallarından ve veri hedeflerinden geçmez, UNS konularında da yayınlanmaz.

## Sonraki adımlar

- [Telemetry API](https://proxus.io/tr/docs/develop/telemetry-api): aktarılan verileri kontrol edin.
- [Trend çalışma alanı](https://proxus.io/tr/docs/monitor/trend-workspace): aktarılan değerleri canlı değerlerle birlikte görüntüleyin.
- [Ağ portları ve uç noktalar](https://proxus.io/tr/docs/reference/ports): Edge Gateway'in HTTP portunu ve diğer adresleri görün.
