## ArrigoFolder format
This is a rough working-copy of the ArrigoFolder format at this point in time.
```xml
<?xml version="1.0" encoding="utf-8"?>
<EXOconfigData format="ArrigoFolder" version="2017.5.000.236">
<Object type="ArrigoFolder">
<Object type="GlobalArguments">
<Attribute name="Name">GlobalArguments</Attribute>
<Attribute name="Comment">This folder contains all arguments for the Published Variable List files. The arguments can be sent to the Pvl file when it is used in run-time.</Attribute>
<Object type="GlobalArgument">
<Attribute name="Name">UnnamedGlobalArgument</Attribute>
<Attribute name="Title">Title</Attribute>
<Attribute name="Comment">Comment</Attribute>
<Attribute name="Default">Default</Attribute>
<Attribute name="Suggests">Suggests</Attribute>
<Attribute name="Editable">Yes</Attribute>
<Attribute name="Inherit">Yes</Attribute>
</Object>
</Object>
<Object type="PublVarFiles">
<Attribute name="Name">Published Variable List files</Attribute>
<Attribute name="Comment">This folder contains all Published Variable List files for the area. Each file exposes variables with names for external applications using the EOS API.</Attribute>
<Object type="PublVarFile">
<Attribute name="Name">SystemAir</Attribute>
<Attribute name="File">Area:\SystemAir.Pvl</Attribute>
<Attribute name="FileArguments">FileArguments</Attribute>
<Attribute name="Comment">Comment</Attribute>
</Object>
<Object type="PublVarFile">
<Attribute name="Name">Room 1</Attribute>
<Attribute name="File">Area:\Rooms.Pvl</Attribute>
<Attribute name="FileArguments">{RoomNumber:1}</Attribute>
<Attribute name="Comment">Comment</Attribute>
</Object>
<Object type="PublVarFile">
<Attribute name="Name">Room 2</Attribute>
<Attribute name="File">Area:\Rooms.Pvl</Attribute>
<Attribute name="FileArguments">{RoomNumber:2}</Attribute>
<Attribute name="Comment">Comment</Attribute>
<!--
<Attribute name="UserDescription">A user description. Should be visible in GraphQl (next sprint)</Attribute>
<Attribute name="Access">Operator (this is the default, we think)(next sprint)</Attribute>
-->
</Object>
</Object>
</Object>
</EXOconfigData>
```
## Iterating Pvl files for an area
UserArea contains the field `publicVariableFiles` which exposes two sub-fields; **name** and **uid**.
| Field | Description |
| -------- | -------- |
| **name** | The name of the exoconfig object in which the file is specified (as read from the ArrigoFolder.ExoXml). <br/>The actual file reference is hidden for the user. |
| **uid** | A 32 char Sha256 hash on:<br/>* Username<br/>* FileName<br/>* Area<br/>* Path (probably deprecated)<br/>* FileArguments |
ESF creates an object containing the "hash based" properties and ties it into a HashMap keyed on the created `uid`. By doing this the ESF can later on retrieve the information when it is time for instantiation. And by saving the user we can also compare later on (when requesting data from the `uid`) that the passed user name is the same as the stored.
*This does not work at the moment as we have no way of getting the token from a subscription.*
This object also contains the actual file contents (parsed, macro'ed and argument'ed) along with advise lists etc.
When a data request comes in for a `uid` the Function does the usual sanity checks before checking if the support object has a proper instance of the file behind (i.e. if it's null or not). If null, we read the file, apply arguments and macros and then return it to the paying customer.
```json
{
folder(id: "TGFuZHNrcm9uYQ==") {
name
...on UserArea {
publicVariableList {
name
uid
}
}
}
}
```
returns
```json
{
"data": {
"folder": {
"name": "Landskrona",
"publicVariableList": [
{
"name": "SystemAir",
"uid": "09E221DBBE078D19D192C7AAA2DC8BE8E5B1A96FF808FE91A1A3935DC8A7CF75"
},
{
"name": "ReginPvl",
"uid": "09E377DBBE078D19D192C7AAA2DC8BE8E5B1A96FF808FE91A1A3935DC8A7CF75"
}
]
}
}
}
```
With the **uid**, and the query `data`, you can request the rest of the static data from the pvl.
```json
{
data(uid:"09E377DBBE078D19D192C7AAA2DC8BE8E5B1A96FF808FE91A1A3935DC8A7CF75") {
content
}
}
```
returns
```json
{
"data": {
"data": {
"content": {
"Cwl": {
"type": "PublishedVariables",
"attributes": {
"Name": "SystemAir",
"Title": "SystemAir",
"Description": "",
"Comment": ""
},
"children": [
{
"type": "PublishedVariable",
"attributes": {
"Name": "Second",
"Title": "Second",
"Description": "Real time clock: Second (0-59)",
"Unit": "s",
"Variable": "((undefined))",
"Writable": "Yes",
"ReadAccess": "Guest",
"WriteAccess": "Guest",
"Comment": "",
"VisualFilter": "1"
}
}
],
"Advise": {
"A": [
"0:0"
]
},
"AdviseRefs": {
"A": [
[
0
]
]
},
"ElementRefs": [
[
[
0
],
4
]
]
}
}
}
}
}
```
ElementRefs contains a list of indexes to find the right element and attribute in the tree.
Ex:
[0],4 means the first child (index 0) and the fifth attribute ("variable") (index 4) in the tree
The `uid` also lets you set up a subscription for updates:
```json
subscription {
data(uid: "09E377DBBE078D19D192C7AAA2DC8BE8E5B1A96FF808FE91A1A3935DC8A7CF75") {
value
path
variable
type
timestamp
}
}
```
Each advise update is then delivered, one at a time, in the following format:
```json
{
"data": {
"data": {
"value": 12,
"path": "Cwl.Advise.A[0]",
"variable": "MyController.QSystem.Sec",
"type": "Update",
"timeStamp": "2019-09-26T12:00:12"
}
}
}
```
### Gray- and blacklisted variable
When subscribing to `uid`s containing gray- or blacklisted variables, the timestamp field indicates when the variable was put in the list. Please note that graylisted variables _might_ start publishing updates if they are "resolved" at a later point in time.
## In the DataStore
The DataStore (DS) has its own cache of variables. When a `read` is requested and it's a cache miss, DS fetches the variable over Wamp and creates a new DataSource in the cache (keyed on the variable (technical address)). It then returns an ISubject reference (of the DataSource) to the callee. The callee can subscribe to updates on the ISubject, and when it's no longer needed the callee just disposes it. Thanks to the internal ref counting in IObservables the DS can handle re-reads of variables about to expire just by checking if the affected DataSource still has observers or not. If a variable expires, and no one is interested in it, the backing DataSource is removed from the cache.
The ISubject in the DataSource is a ReplaySubject which stores its last message (if there is one), and upon a new subscription immediately publishes it to the subscriber.
## Workflow
Just because you have an `uid` doesn't mean that you just can set up a subscription directly and expect everything to work.
The `uid` is created/registered in the ESF when you do a `folder ..on UserArea{ publicVariableList}` query. Once that is done you can go ahead and either get the contents of the file or setup a subscription. As long as the ESF isn't restarted you can "reuse" the uid and start a subscription directly (without the `folder` query), even between "sessions".
But if the ESF doesn't know about the `uid` (or if it is invalid) you get a GraphQL error when querying either `data` or `subscription/data`.
## Questions
How unique should the uid be? Forever? Something hashed based on the user?
What should happen when you request data for an uid that is not instantiated in the Function? This is dependant on the answer of the question above. If the uid is longlived and we restart something the hashmap in the API (and the Function?) is destroyed. Should we the be able to use our longlived uid to directly get data or do we need to get a new uid?