10.4. File System Data Model
10.4.1. General description
File System Data Model feature is implemented in a separate application called Svetovid that uses Anjay library for communication with a LwM2M server. Svetovid runs on any Linux-based system what makes it ideal for quick prototyping and integration with existing infrastructure. File System Data Model is easy to use. After installing Svetovid LwM2M objects can be represented as scripts on the file system using any programming language. This allows creating LwM2M objects without C/C++ programming knowledge. Additionally the File System Data Model comes with a stub generator for Python and shell script to create a LwM2M object template.
10.4.2. Supported features
The following features are implemented:
Mapping of directory on the file system to LwM2M Objects, Instances and Resources
Handling of multi-instance objects
FSDM key-value store for keeping volatile object state
Object stub generator for Python and sh to get started easily
10.4.3. Technical documentation
10.4.3.1. Directory mapping
When enabled, File System Data Model maps a specific directory to LwM2M Objects,
Instances and Resources. Default mapped directory is /etc/svetovid/dm
but it
can be changed in the /etc/svetovid/config/fsdm.json
file. The mapped directory
is expected to have the following structure:
/etc/svetovid/dm/
(default)$OBJECT_ID/
- directory representing a LwM2M Object with given ID.resources/
- directory containing scripts used to access individual Resources.$RESOURCE_ID
- executable scripts representing individual Resource of a given ID.
instances
- (optional) executable script used for managing object instances.transaction
- (optional) executable script used to handle transactional processing of object resources.
10.4.3.2. Installing Svetovid
To install Svetovid from source and use File System Data Model run those commands:
$ ./devconfig -DTARGET_PLATFORM=[TARGET]
$ make
$ sudo make install
Note
Use “generic” as TARGET
to compile Svetovid on a standard Linux distribution.
Note
Note that this installs a svetovid.service
systemd service, automatically
enabled, and starts-up the Client immediately. You may want to disable the
service using systemctl command.
10.4.3.3. Configuring Svetovid
Svetovid is configured using JSONs files. The default location of those JSONs is
/etc/svetovid/config
.
Note
Default configuration directory may be overwritten by passing --conf-dir
as
a command line argument when starting a Svetovid binary.
The configuration is stored in those files:
security.json
- represents the Security LwM2M objectserver.json
- represents the Server LwM2M objectsvd.json
- global settings
Example of security.json
:
{
"2": {
"server_uri": "coaps://eu.iot.avsystem.cloud:5684",
"security_mode": "psk",
"pubkey_or_identity_hex": "6d7a2d737665746f766964",
"privkey_or_psk_hex": "737665746f76696431323334",
"ssid": "7",
"is_bootstrap": "0"
}
}
Example of server.json
:
{
"0": {
"ssid": "7",
"lifetime": "60",
"binding": "U"
}
}
Example of svd.json
:
{
"device": {
"endpoint_name": "svetovid-example"
},
"logging": {
"default_log_level": "info",
"log_level": {
"svd": "debug"
}
},
"in_buffer_size_b": 10240,
"out_buffer_size_b": 10240,
"msg_cache_size_b": 65536
}
Note
For detailed description of configuration files format please refer to the full documentation.
10.4.3.4. Developing custom object example
After installation Svetovid is run as a service and can be controlled by systemctl
To implement a Time Object (/3333) you can start by generating a stub
$ sudo svetovid-fsdmtool generate --object 3333 --output-dir /etc/svetovid/dm --generator python
This will create /etc/svetovid/dm/3333
directory containing Python scripts
that represent LwM2M resources. The hierarchy of the filesystem mapped to a LwM2M
object is:
/etc/svetovid/dm/3333
├── Application_Type -> resources/5750
├── Current_Time -> resources/5506
├── Fractional_Time -> resources/5507
├── instances
├── Measurement_Quality_Indicator -> resources/6042
├── Measurement_Quality_Level -> resources/6049
└── resources
├── 5506
├── 5507
├── 5750
├── 6042
└── 6049
The scripts generated this way contain placeholders for the read
, write
and reset
functions that need to be filled out by the user. For example
the Fraction Time
(/3333/*/5507) resource can be implemented as:
#!/usr/bin/env python
# -*- encoding: utf-8 -*-
from fsdm import ResourceHandler, CoapError, DataType, KvStore
class ResourceHandler_3333_5506(ResourceHandler):
NAME = "Current Time"
DESCRIPTION = '''\
Unix Time. A signed integer representing the number of seconds since
* Jan 1st, 1970 in the UTC time zone.'''
DATATYPE = DataType.TIME
EXTERNAL_NOTIFY = False
def read(self,
instance_id, # int
resource_instance_id): # int for multiple resources, None otherwise
# It's just that simple!
import time
sys.stdout.write(str(int(time.time())))
def write(self,
instance_id, # int
resource_instance_id): # int for multiple resources, None otherwise
# NOTE: Implement this if you want to be able to change time on your system.
raise CoapError.NOT_IMPLEMENTED
def reset(self,
instance_id): # int
# NOTE: reset resource to its original state. You can either set it to
# a default value or delete the resource.
pass
if __name__ == '__main__':
ResourceHandler_3333_5506().main()
To implement Application Type
resource (/3333/*/5750) we can use a simple
implementation of a key-value store which is accessible from Python via KvStore
class. The class implements a simple interface:
KvStore(namespace)
- constructor that takesnamespace
as an argument. This can be set as anything as long as it can be uniquely distinguished between different objects. A good idea is to use an Object ID as anamespace
used by theKvStore
.get(key, default=None)
- method for getting a value for a givenkey
. If the value is not present it will return thedefault
value.set(key, value)
- method for setting avalue
for a givenkey
.delete(key)
- method for deleting akey
with associated value from theKvStore
.
#!/usr/bin/env python
# -*- encoding: utf-8 -*-
from fsdm import ResourceHandler, CoapError, DataType, KvStore
import sys # for sys.stdout.write() and sys.stdin.read()
class ResourceHandler_3333_5750(ResourceHandler):
NAME = "Application Type"
DESCRIPTION = '''\
The application type of the sensor or actuator as a string depending
* on the use case.'''
DATATYPE = DataType.STRING
EXTERNAL_NOTIFY = False
def read(self,
instance_id, # int
resource_instance_id): # int for multiple resources, None otherwise
value = KvStore(namespace=3333).get('application_type')
if value is None:
# The value was not set, so it's not found.
raise CoapError.NOT_FOUND
# The value is present within the store, thus we can print it on stdout.
# The important thing here is to remember to return string-typed resources
# with sys.stdout.write(), as print() adds unnecessary newline character, so
# if we used it instead, the value presented to the server would contain that
# trailing newline character.
sys.stdout.write(value)
def write(self,
instance_id, # int
resource_instance_id): # int for multiple resources, None otherwise
# All we need to do is to assign a value to the application_type key.
KvStore(namespace=3333).set('application_type', sys.stdin.read())
def reset(self,
instance_id): # int
# We reset the resource to its original state by simply deleting the application_type
# key
KvStore(namespace=3333).delete('application_type')
if __name__ == '__main__':
ResourceHandler_3333_5750().main()