# DeviceTalk 資料庫說明
## 總覽
### api.models
* Language
* BasicFile
* Library
* DfType
* LibraryFunction
* SaFunction
* DeviceLibrary
* Device
* DeviceHasLibrary
* DeviceDf
* DeviceLibraryDf
* DeviceDfHasSaFunction
* LibraryDfHasSaFunction
### file_handle.models
* File
* UploadBatch
## ERD

## 詳細說明
### api.models.Language
記錄 DeviceTalk 中有哪些程式語言
* id: `<PK>`
* name: `char(30)` name of the Language
| id | name |
| ----- | ------- |
| 1 | Python |
| 2 | C++ |
### api.models.BasicFile
繼承 `api.models.FileGroup`
Basic file 是產生 SA code 的模板,在現在版本中,每種 Langugage 只會對應一個 BasicFile。考慮到未來擴充,將其對應關係定為一對多。
* id: `<PK>`
* name: `char(100)` name of this basic file
* dir_path: `char(255)` 此 basic file 在的 SA code 模板的根目錄路徑。一般訂為
* language: `<FK>`. Language
### api.models.Library
繼承 `api.models.FileGroup`
Library 指的是由使用者上傳的。
* id: `<PK>`
* name: `char(100)` name of this basic file
* dir_path: `char(255)` 此 library 根目錄相對於 SA code 模板的根目錄相對路徑。
* basic_file: `<FK>` BasicFile
* global_var_setup: `str` global variable 的內容
* gvs_readonly_lines: `JSON` global variable 中哪幾行是 readonly
### api.models.LibraryFunction
由使用者上傳的 Library 中的範例程式內容。顯示在 DeviceTalk GUI 的 DF Function Manager 左上列表中。
* id: `<PK>`
* name: `char(100)` name of this library function
* library: `FK` Library
接下來有數個 entry,皆為`str`,預設為`''`。表示 LibraryFunction 中有哪些 session。被定義在 `settings.LIBRARYFUNCTION_FIELDS` 中,預設為 `['var_define', 'import_string', 'init_content','runs_content']`
### api.models.DfType
定義 SaFunction, DeviceFeature 的 DF Type
* id: `<PK>`
* df_type: `union` (`idf`, `odf`)
* params: `JSON` 各參數型態的 list
| id | df_type | params |
| ----- | ---- |----|
| 1 | idf | ['int'] |
| 2 | odf | ['float', 'float'] |
### api.models.SaFunction
SAFunction 相較於 LibraryFunction,是由使用者在 DeviceTalk 上依照模板產生的 function。可以直接做為,DF在執行時呼叫的 function。
* id: `<PK>`
* name: `char(100)` name of this sa function
* var_setup: `JSON` 此 function variable setup 的預設值
* code: `JSON` 此 function 的內容
* readonly_lines: `JSON` code 中哪幾行是 readonly
* updated_at: `datetime` 最後更新時間
* function_type: `FK` DfType
* library_ref: `FK` LibraryFunction,可為 `None`。代表此 SaFunction 是依照哪個 LibraryFunction 產生的。
### api.models.DeviceLibrary
繼承 `api.models.LibraryPoolBase`
DeviceLibrary 相較於 Library,是由 DeviceTalk 產生的 library。
* id: `<PK>`
* name: `char(100)` name of this device library
* basic_file: `FK` DasicFile。代表此 DeviceLibrary 依據哪個 BasicFile 生成。
* user: `FK` User。代表由哪個 User 產生。
* global_var_setup: `str` global variable 的內容
* gvs_readonly_lines: `JSON` global variable 中哪幾行是 readonly
* dir_path: `char(255)` 此 device library 根目錄相對於 SA code 模板的根目錄相對路徑。
* dependency_library `M2M` Library。這個 DeviceLibrary 使用哪些 Library。
* functions `M2M` SaFunction。這個 DeviceLibrary 中包含哪些 SaFunction。
### api.models.Device
繼承 `api.models.LibraryPoolBase`
搭配 `api.models.DeviceHasLibrary`, `api.models.DeviceDf` 記錄 IoTalk device 的狀態。其他細節參考[Device 相關資訊儲存說明](#Device-相關資訊儲存說明)
* id: `<PK>`
* name: `char(100)` name of this device library
* dm_name: `char(100)` name of device model
* user: `FK` User。代表由哪個 User 產生。若為 `None` 代表此 device 是 global 的。所有使用者都看得到。
* basic_file: `FK` DasicFile。代表此 DeviceLibrary 依據哪個 BasicFile 生成。
* global_var_setup: `str` global variable 的內容
* gvs_readonly_lines: `JSON` global variable 中哪幾行是 readonly
* functions `M2M` SaFunction。這個 Device 中包含哪些 SaFunction。
* server_url: `char(255)`。DeviceTalk GUI 中 DA Tab 的 IoTtalk Server 的內容。
* device_address: `char(50)`。DeviceTalk GUI 中 DA Tab 的 Device Address 的內容。
* push_interval: `float`。DeviceTalk GUI 中 DA Tab 的 Device Address 的內容。
### api.models.DeviceHasLibrary
這個用來記錄各個 Device 有哪些 Library 的關係表。
* id: `<PK>`
* device: `FK` Device
* device_library: `FK` DeviceLibrary
* library: `FK` Library
* order: `int` 用來記錄使用者點及 library 的順序
### api.models.DeviceDf
繼承 `DeviceFeatureBase`
* id: `PK`
* name: the name of this device feature
* df_type: `FK` DfType
* device: `FK` Device。說明這個 DF 屬於哪個 Device
* functions: `M2M` SaFunction through DeviceDfHasSaFunction
### api.models.DeviceLibraryDf
繼承 `DeviceFeatureBase`
* id: `PK`
* name: the name of this device feature
* df_type: `FK` DfType
* device_library: `FK` DeviceLibrary。說明這個 DF 屬於哪個 DeviceLibrary
* functions: `M2M` SaFunction through LibraryDeviceDfHasSaFunction
在 DeviceTalk 實作中,Device Feature 是各個 Device 分別記錄,也就是相同 DM 的兩個 Device,會有對應的兩組 DeviceDf。
### api.models.DeviceDfHasSaFunction
繼承 `DfHasSaFunctionBase`
* id: `PK`
* function: `FK` SaFunction
* device: `FK` Device
* var_setup: `str` 此 DF 與 SaFunction 關係中的 variable setup 內容。
* selected: `boolean` 此 DF 是否選擇此 SaFunction。
### api.models.DeviceDfHasSaFunction
繼承 `DfHasSaFunctionBase`
* id: `PK`
* function: `FK` SaFunction
* device_library: `FK` DeviceLibrary
* var_setup: `str` 此 DF 與 SaFunction 關係中的 variable setup 內容。
* selected: `boolean` 此 DF 是否選擇此 SaFunction。
DeviceFeature 中的 functions 紀錄此 DF 中包含哪些 SaFunction,但是在關係中,還有 `var_setup`, `selected` 兩個屬性,故自行實作 RelationTable
### file_handle.models.UploadBatch
單純紀錄每次上傳程序中,被上傳資料夾中的所有檔案物件
* id: `PK`
### file_handle.models.File
* id: `PK`
* file_path: `char(100)`
* real_path: `char(200)`
* is_upload: `boolean` 此檔案是否已上傳,若被建立超過一天仍為未上傳,在 scheduler 執行 cleanup 時會被清除。
* uuid: `UUID4` 此檔案的 uuid,必須是唯一的。
* created_at: `Datetime` 建立此 Entry 的時間
* updated_at: `Datetime` 最後更新此 Entry 的時間
* upload_batch: `FK` UploadBatch
* library: `FK` Library
* device_library: `FK` DeviceLibrary
* basic_file: `FK` BasicFile
* device: `one2one` Device
DeviceTalk 的檔案管理實作中,藉由這個表格,讓所有有檔案的 table,在處理時,不用額外擔心實際的檔案純儲存在 server 的哪個路徑。
DeviceTalk 將所有檔案都放在 `<uploaddir>` 下,藉由將檔名透過 uuid 編碼,就可以不用擔心相同檔名衝突的問題。像是下表的`File:100`和`File:104`,檔名(file_path)都是`config.ini`,但屬於不同的 BasicFile。
| id | file_path | real_path | is_upload | uuid | library | basic_file | ... |
| --- | ------ |--------------- | ------- | ----- | ----- | ----- | ----- |
| 100 | config.ini | `<uploaddir>/abc1...` | T | abc1... | None | 1 | ... |
| 101 | sa.py | `<uploaddir>/abc2...` | T | abc2... | None | 1 | ... |
| 102 | Makefile | `<uploaddir>/abc3...` | T | abc3... | None | 1 | ... |
| 103 | library/lib.py | `<uploaddir>/abc4...` | T | abc4... | None | 1 | ... |
| 104 | config.ini | `<uploaddir>/abc5...` | T | abc5... | None | 2 | ... |
| 105 | sa.cpp | `<uploaddir>/abc6...` | T | abc6... | None | 2 | ... |
| 106 | Makefile | `<uploaddir>/abc7...` | T | abc7... | None | 2 | ... |
同時 file_handle.models.File 中提中 open 的 function,讓開發者可以直接以抽象的檔案架構中讀寫某檔案。
例如 `basicfile1_object.file_set.get(file_name='config.ini').open('r')` 意義等同 `open('<uploaddir>/abc1...', 'r')`
## Device 相關資訊儲存說明

上圖為與儲存 Device 資訊相關的 table
### Device 與 Library 的關係
在 DeviceTalk GUI 的 Library Selection 中顯示的 Library,在實作上,分成`Library`(table.d)及`DeviceLibrary`(table.c)來儲存,所以無法用單一的`M2M`關係來紀錄其關係。因此實作`DeviceHasLibrary`(table.f)來表示。
例如,下表代表`<Device:10>`依序選擇了`<Library:5>`, `<Library:3>`, `<DeviceLibrary:3>`,對應的`DeviceHasLibrary`中,會記錄的資料。
| id | device | device_library | library | order |
| --- | ------ |--------------- | ------- | ----- |
| 100 | 10 | null | 5 | 0 |
| 101 | 10 | null | 3 | 1 |
| 102 | 10 | 3 | null | 2 |
同一個 entry,`device_library`及`library`必需恰有一者為`None`,`order` 越大代表使用者越晚勾選的 library。
### Device 與 Function 的關係
* 在 DeviceTalk GUI 的 DF Function Manager 中顯示的 Function。
* library function: 左上列表。對應 `LibraryFunction` (table.e)
* SA function: 右上列表。對應 `SaFunction` (table.b)
* 差異
* SA function 在一個 device 中,其內容是可以被使用者新增、修正、刪除的,因此建立一個M2M關係(Relation.1)來記錄。
* 因為 library function 自上傳後,內容就固定,因此不需要額外紀錄`Device`與`LibraryFunction`的關係,可以透過 `Device` -> `DeviceHasLibrary` -> `Library` -> `LibraryFunction` 的關係,來獲得某 device 有哪些的 library function。
### Device、DF 與 SaFunction的關係
* 在每個`Device`建立時,DeviceTalk會為其新增他的DM中的所有DF,紀錄在 `DeviceDf`(table.g) 中。
* 例如: 有個`DM`包含`DF1`, `DF2`, `DF3`,在建立時,這三個DF都會被建立,即便說此 Device,對應在 IoTtalk Project 上的 Device Object,只使用了`DF1`。而 DeviceTalk GUI 顯示的 DF 會與 Device Object 相同。
* 在同一個 Device 中,允許超過一個 DF 使用相同的 SaFunction,但可以搭配不同的 variable setup 內容,而 `DeviceDfHasSaFunction`(table.h) 就是用來紀錄這些差異,除了 variable setup 外,同時也記錄了每個 DF 選擇哪個 SaFunction。
* 為何有了`DF`與`SaFunction`的關係(Relation.3~5),還需要紀錄 Relation.1呢?
因為 Relation.3~5 只紀錄各DF選擇過哪些`SaFunction`,Relation.1 還紀錄其他沒被使用的`SaFunction`,在儲存成 device library 時會一起被紀錄,供其他使用者使用。
### 儲存 Device 時產生的 Device Library
在每個 device 儲存時,除了更新上述對應的資料,也會以該 device 為名,建立一個 library,這個 library 屬於`DeviceLibrary`。
#### DF
* 新建立的`DeviceLibrary`會複製`Device`中所有的 DF 關聯資訊與 SaFunction 關聯資訊。其對應關係分別是:
* `DeviceDf`(table.g) -> `DeviceLibraryDf`(table.i)
* `DeviceDfHasSaFunction`(table.h) -> `DeviceLibraryDfHasSaFunction`(table.j)
* Relation.1 -> Relation.2
* `DeviceLibrary`做為 library 之所以會需要紀錄 DF 資訊,是方便下次其他使用者使用此 `DeviceLibrary`,能匯入 DF 與 SaFunction 資訊,縮短使用者設定的時間。
* 因為`DeviceLibrary`對於 DeviceTalk GUI,是不分 Device Model 的,因此當`DeviceLibrary`被更新時,DF 資訊會疊加。
#### Library
* 由於`Device`可以有 dependency library,同理`DeviceLibrary`也需紀錄其 dependency library。儲存時的邏輯如下
* 將`Device`與`Library`的關係(Relation.10~11),記錄在新的`DeviceLibrary`的 Relation.12 中。
* 將`Device`與`DeviceLibrary`的關係,為避免過多層的巢狀呼叫,不紀錄,而是直接合併其中所有的`SaFunction`,原則上取聯集,當兩個`SaFunction`的`DfType`與`name`相同時,以較新的為準。