Backend Gateway ↔ Child Association
Association Authority در Backend نگهداری میشود و Firmware فقط Current effective state را از Onboarding/Sync دریافت میکند.
مدل Canonical
GatewayChildAssociation در IOT.App:
Id Guid
ProjectId Guid (real relational FK)
GatewayDeviceSerialId Guid (cross-service reference)
ChildDeviceSerialId Guid (cross-service reference)
BoundAtUtc DateTime
UnboundAtUtc DateTime?
UnbindReason bounded string
ConcurrencyToken rowversion
GatewayDeviceSerialId و ChildDeviceSerialId به IOT.Center.DeviceSerial.Id اشاره میکنند؛ چون دیتابیس Serviceها جداست SQL FK بین آنها وجود ندارد.
Active association
UnboundAtUtc == null
SQL Server filtered unique index تضمین میکند هر Child حداکثر یک Association فعال داشته باشد:
builder.HasIndex(x => x.ChildDeviceSerialId)
.IsUnique()
.HasFilter("[UnboundAtUtc] IS NULL");
Routing
Child DeviceSerial
→ active GatewayChildAssociation
→ GatewayDeviceSerialId
→ Physical Gateway DeviceSerial
→ dn/cmd
هرگز از latest historical row برای Routing استفاده نکنید.
Delete و Reassociation
- Unbind row را حذف نمیکند؛
UnboundAtUtc/Reason را Set میکند. - Reassociation row قدیمی را میبندد و row جدید میسازد.
- حذف Gateway نباید Child identity/history را حذف کند.
- Factory Reset محلی Gateway بهتنهایی Backend association را تغییر نمیدهد.