Computer Science Experimentation

Saturday, May 16, 2015

F# and MQTT - example 1:

This post presents a F# Interactive Script example of the MQTT protocol.
Using the same client node, the example does Publish and Subscribe, of Simple and Complex data, using Json and Binary serialization. The following software were used:
-Server: Mosquitto
-Client: M2Mqtt
-Serialization: Json: Json.Net, Binary: FSPickler

MQTT

MQTT is a machine-to-machine (M2M)/"Internet of Things" connectivity protocol. It was designed as an extremely lightweight publish/subscribe messaging transport.
MQTT v3.1.1 has now become an OASIS Standard.
MQTT stands for MQ Telemetry Transport. It is a publish/subscribe, extremely simple and lightweight messaging protocol, designed for constrained devices and low-bandwidth, high-latency or unreliable networks. The design principles are to minimize network bandwidth and device resource requirements whilst also attempting to ensure reliability and some degree of assurance of delivery. These principles also turn out to make the protocol ideal of the emerging “machine-to-machine” (M2M) or “Internet of Things” world of connected devices, and for mobile applications where bandwidth and battery power are at a premium.

Concepts

Publish/Subscribe:

The MQTT protocol is based on the principle of publishing messages and subscribing to topics, or "pub/sub". Multiple clients connect to a broker and subscribe to topics that they are interested in. Clients also connect to the broker and publish messages to topics. Many clients may subscribe to the same topics and do with the information as they please. The broker and MQTT act as a simple, common interface for everything to connect to. This means that you if you have clients that dump subscribed messages to a database, to Twitter or even a simple text file, then it becomes very simple to add new sensors or other data input to a database,Twitter or so on.

Topics/Subscriptions: 

Messages in MQTT are published on topics. There is no need to configure a topic, publishing on it is enough. Topics are treated as a hierarchy, using a slash (/) as a separator. This allows sensible arrangement of common themes to be created, much in the same way as a filesystem. For example, multiple computers may all publish their hard drive temperature information on the following topic, with their own computer and hard drive name being replaced as appropriate:sensors/COMPUTER_NAME/temperature/HARDDRIVE_NAME Clients can receive messages by creating subscriptions. A subscription may be to an explicit topic, in which case only messages to that topic will be received, or it may include wildcards. Two wildcards are available, + or #.
+ can be used as a wildcard for a single level of hierarchy. It could be used with the topic above to get information on all computers and hard drives as follows:sensors/+/temperature/+ # can be used as a wildcard for all remaining levels of hierarchy. This means that it must be the final character in a subscription. With a topic of "a/b/c/d", the following example subscriptions will match: a/b/#, a/b/c/#, +/b/c/# Zero length topic levels are valid.

Clean session / Durable connections:

 On connection, a client sets the "clean session" flag, which is sometimes also known as the "clean start" flag. If clean session is set to false, then the connection is treated as durable. This means that when the client disconnects, any subscriptions it has will remain and any subsequent QoS 1 or 2 messages will be stored until it connects again in the future. If clean session is true, then all subscriptions will be removed for the client when it disconnects. Will: When a client connects to a broker, it may inform the broker that it has a will. This is a message that it wishes the broker to send when the client disconnects unexpectedly. The will message has a topic, QoS and retain status just the same as any other message.

Retained Messages: 

All messages may be set to be retained. This means that the broker will keep the message even after sending it to all current subscribers. If a new subscription is made that matches the topic of the retained message, then the message will be sent to the client. This is useful as a "last known good" mechanism. If a topic is only updated infrequently, then without a retained message, a newly subscribed client may have to wait a long time to receive an update. With a retained message, the client will receive an instant update. Quality of Service: MQTT defines three levels of Quality of Service (QoS). The QoS defines how hard the broker/client will try to ensure that a message is received. Messages may be sent at any QoS level, and clients may attempt to subscribe to topics at any QoS level. This means that the client chooses the maximum QoS it will receive. For example, if a message is published at QoS 2 and a client is subscribed with QoS 0, the message will be delivered to that client with QoS 0. If a second client is also subscribed to the same topic, but with QoS 2, then it will receive the same message but with QoS 2. For a second example, if a client is subscribed with QoS 2 and a message is published on QoS 0, the client will receive it on QoS 0. Higher levels of QoS are more reliable, but involve higher latency and have higher bandwith requirements. 0: The broker/client will deliver the message once, with no confirmation. 1: The broker/client will deliver the message at least once, with confirmation required. 2: The broker/client will deliver the message exactly once by using a four step handshake.

REMARKS

Windows Installation: Mosquitto is installed in "Program Files (x86)" where the files are read-only and requires Admin priviledges. For now, to update the configuration file, make a copy to another folder, do the update and copy it back.

F# Example 1

Script Code:

//M2MqttTest_1.fsx
//Celso Axelrud
//rev.: 5/16/2015-4:15pm

(*
MQTT example 1: Same client node Pub/Sub of simple and complex data using json and binary serialization.
 
Server: Mosquitto
Client: M2Mqtt
Serialization:
    -Json: Json.Net
    -Binary: FSPickler
*)

//Libraries-----------------------
#I @"C:\Project(comp)\Dev_2015\MQTT_1\MQTT_Proj_1\Lib"
#r "M2Mqtt.dll" 
#r "Newtonsoft.Json.dll"
#r "FsPickler.dll"

//Open System---------------------
open System
open System.Text

//Open M2Mqtt---------------------
open uPLibrary.Networking.M2Mqtt
open uPLibrary.Networking.M2Mqtt.Exceptions;
open uPLibrary.Networking.M2Mqtt.Messages;
open uPLibrary.Networking.M2Mqtt.Session;
open uPLibrary.Networking.M2Mqtt.Utility;
open uPLibrary.Networking.M2Mqtt.Internal;

//Create client node--------------
let node = new MqttClient(brokerHostName="localhost")

//Create subscription handles-----
//Received 
let MsgReceived (e:MqttMsgPublishEventArgs) =
    printfn "Sub Received Topic: %s" e.Topic
    printfn "Sub Received Qos: %u" e.QosLevel
    printfn "Sub Received Retain: %b" e.Retain
    printfn "Sub Received Message: %s" (Encoding.ASCII.GetString e.Message)
node.MqttMsgPublishReceived.Add(MsgReceived)

//Publish (requires QoS Level 1 or 2)
let MsgPublish (e:MqttMsgPublishedEventArgs) =
    printfn "Pub Message Published: %b " e.IsPublished
node.MqttMsgPublished.Add(MsgPublish)

//Subscribed (requires QoS Level 1 or 2)
let MsgSubscribed (e:MqttMsgSubscribedEventArgs) =
    printfn "Sub Message Subscribed: %s " (Encoding.ASCII.GetString e.GrantedQoSLevels) 
node.MqttMsgSubscribed.Add(MsgSubscribed)

//Unsubscribed (requires QoS Level 1 or 2) 
let MsgUnsubscribed (e:MqttMsgUnsubscribedEventArgs) =
    printfn "Sub Message Unsubscribed: %i " e.MessageId 
node.MqttMsgUnsubscribed.Add(MsgUnsubscribed)

//Connect-------------------------
node.Connect(clientId="Node1Conn1",username="caxelrud",password="laranja1",
                willRetain=false,willQosLevel=0uy,willFlag=true,willTopic="system/LWT/Node1Conn1",willMessage="offline",
                cleanSession=true,keepAlivePeriod=60us)

//Get node info (Interactive)-----
node;;
node.CleanSession;; node.ClientId;; node.Settings;;
node.WillFlag;; node.WillQosLevel;;
node.WillTopic;; node.WillMessage;;

//Subscribe-----------------------
let  topics1:string[] = [| "sensor/A10/TI100"; "sensor/A10/FI001" |]
let qosLevels1:byte[] = [|MqttMsgBase.QOS_LEVEL_AT_MOST_ONCE; MqttMsgBase.QOS_LEVEL_AT_MOST_ONCE |]
let grantedQos1 = node.Subscribe(topics1, qosLevels1)

//Subscribe to Connection Last Will 
let topics2:string[] = [| "system/LWT/Node1Conn1" |]
let qosLevels2:byte[] = [|MqttMsgBase.QOS_LEVEL_AT_MOST_ONCE |]
let grantedQos2 = node.Subscribe(topics2, qosLevels2)

//Publish-------------------------
let mutable Temp1="100.0"
node.Publish("sensor/A10/TI100", Encoding.UTF8.GetBytes(Temp1))

//Publish-------------------------
Temp1<-"200.0"
node.Publish("sensor/A10/TI100", Encoding.UTF8.GetBytes(Temp1))

//Publish but no notification because we didn't subscribe to this point
node.Publish("sensor/A11/TI101", Encoding.UTF8.GetBytes("1.0"))

//Complex Messages----------------
//json----------------------------
open Newtonsoft.Json

//Type for a group of tags with group name and group date & time
type Tag={Name:string;Value:float;Qual:int}
type Rec={Name:string;Time:System.DateTime;Tags:Tag list}

//Subscribe-----------------------
let  topics3:string[] = [| "sensor/A10/Group1" |]
let qosLevels3:byte[] = [|MqttMsgBase.QOS_LEVEL_AT_MOST_ONCE|]
let grantedQos3 = node.Subscribe(topics3, qosLevels3)

//Generate random values----------
let values1=[for i in 1..2 -> System.Random().NextDouble() ]

//Create the complex point--------
let tags1 = [
        { Name = "PI100"; Value = values1.[0];Qual=0 };
        { Name = "PI101"; Value = values1.[1];Qual=0 }
    ]
let grp1={Name="Group1";Time=System.DateTime.UtcNow;Tags=tags1}

//Serialize-----------------------
let grp1j = JsonConvert.SerializeObject(grp1)
//Deserialize (test)-------------- 
let grp1d = JsonConvert.DeserializeObject<Rec>(grp1j)
//Publish-------------------------
node.Publish("sensor/A10/Group1", message=Encoding.UTF8.GetBytes(grp1j),qosLevel=0uy,retain=true)

//binary serialization---------------
open Nessos.FsPickler

//Subscribe-----------------------
let  topics4:string[] = [| "sensor/A10/Group2" |]
let qosLevels4:byte[] = [|MqttMsgBase.QOS_LEVEL_AT_MOST_ONCE|]
let grantedQos4 = node.Subscribe(topics4, qosLevels4)

//Create the complex point--------
let grp2={Name="Group2";Time=System.DateTime.UtcNow;Tags=tags1}
let binary = FsPickler.CreateBinary()

//Serialize-----------------------
let grp1b=binary.Pickle grp2

//Deserialize (test)-------------- 
let grp1db=binary.UnPickle<Rec> grp1b

//Publish-------------------------
node.Publish("sensor/A10/Group2", message=grp1b,qosLevel=0uy,retain=true)

//Unsubscribe---------------------
node.Unsubscribe(topics1)
node.Unsubscribe(topics2)
node.Unsubscribe(topics3)
node.Unsubscribe(topics4)

//Client Disconnect---------------
node.Disconnect()

Mosquitto Parameters:

#retry_interval 20 #sys_interval 10 #store_clean_interval 10 #pid_file #user mosquitto #max_inflight_messages 20 #max_queued_messages 100 #queue_qos0_messages false #message_size_limit 0 #allow_zero_length_clientid true #auto_id_prefix #persistent_client_expiration #allow_duplicate_messages false #upgrade_outgoing_qos false #bind_address #port 1883 #max_connections -1 #protocol mqtt #http_dir #use_username_as_clientid #cafile #capath #certfile #keyfile #tls_version #require_certificate false #use_identity_as_username false #crlfile #ciphers DEFAULT:!aNULL:!eNULL:!LOW:!EXPORT:!SSLv2:@STRENGTH #psk_hint #ciphers #listener #max_connections -1 #mount_point #protocol mqtt #http_dir #use_username_as_clientid #cafile #capath #certfile #keyfile #require_certificate false #crlfile #ciphers #psk_hint #ciphers #autosave_interval 1800 #autosave_on_changes false #persistence false #persistence_file mosquitto.db #persistence_location #log_dest stderr #log_facility #log_type error #log_type warning #log_type notice #log_type information #websockets_log_level 0 #connection_messages true #log_timestamp true #clientid_prefixes #auth_plugin #password_file #psk_file #acl_file #connection <name> #address <host>[:<port>] [<host>[:<port>]] #topic <topic> [[[out | in | both] qos-level] local-prefix remote-prefix] #bridge_attempt_unsubscribe true #round_robin false #remote_clientid #local_clientid #cleansession false #notifications true #notification_topic #keepalive_interval 60 #start_type automatic #restart_timeout 30 #idle_timeout 60 #threshold 10 #try_private true #remote_username #remote_password #bridge_cafile #bridge_capath #bridge_certfile #bridge_keyfile #bridge_insecure false #bridge_identity #bridge_psk #include_dir #ffdc_output #max_log_entries #trace_level #trace_output *)

Monday, December 9, 2013

S4M - Experimental Supervisory System for Manufacturing – Calculation Engine



Introduction

This document describes a Calculation Engine to be used in supervisory systems for process and discrete manufacturing.

This system is composed by the following components:
- Data-structures in-memory servers (mem-node)
- Computational engines (eng-node)
- Web visualization (vis-node)
- Database servers (db-node)

S4M is oriented to engineers with limited knowledge of computer science. Because of that, the selection of one simple computer language, to be used in all system’s components, was required. S4M uses Google Dart language.

The mem-nodes are responsible for fast data storage and retrieve.

Data-structures in-memory server allows the storage and retrieve, using keys, of data-structure (string, numbers, lists, sets, hashes). Data is periodic persisted.
S4M uses Redis (http://redis.io ).

Redis describes itself as:
Redis is an open source, BSD licensed, advanced key-value store. It is often referred to as a data structure server since keys can contain strings, hashes, lists, sets and sorted sets.
In order to achieve its outstanding performance, Redis works with an in-memory dataset. Depending on your use case, you can persist it either by dumping the dataset to disk every once in a while, or by appending each command to a log.
Redis also supports trivial-to-setup master-slave replication, with very fast non-blocking first synchronization, auto-reconnection on net split and so forth.
Other features include Transactions, Pub/Sub, Lua scripting, Keys with a limited time-to-live, and configuration settings to make Redis behave like a cache.
You can use Redis from most programming languages out there.
Redis is written in ANSI C and works in most POSIX systems like Linux, *BSD, OS X without external dependencies. Linux and OSX are the two operating systems where Redis is developed and more tested, and we recommend using Linux for deploying. Redis may work in Solaris-derived systems like SmartOS, but the support is best effort. There is no official support for Windows builds, but Microsoft develops and maintains a Win32-64 experimental version of Redis.

Redis is ranked 13 in database popularity (11/16/2013) in http://db-engines.com/en/ranking.

The eng-nodes are responsible for all required computation.
S4M uses Google Dart (http://www.dartlang.com ).
Dart describes itself as:
Dart is easy to learn. A wide range of developers can learn Dart quickly. It’s an object-oriented language with classes, single inheritance, lexical scope, top-level functions, and a familiar syntax. Most developers are up and running with Dart in just a few hours.
Dart compiles to JavaScript. Dart has been designed from the start to compile to JavaScript, so that Dart apps can run across the entire modern web. Every feature considered for the language must somehow be translated to performant and logical JavaScript before it is added. Dart draws a line in the sand and doesn’t support older, legacy browsers.
Dart runs in the client and on the server. The Dart virtual machine (VM) can be integrated into a web browser, but it can also run standalone on the command line. With built-in library support for files, directories, sockets, and even web servers, you can use Dart for full end-to-end apps.
Dart comes with a lightweight editor. You can use Dart Editor to write, launch, and debug Dart apps. The editor can help you with code completion, detecting potential bugs, debugging both command-line and web apps, and even refactoring. Dart Editor isn’t required for writing Dart; it’s just a tool that can help you write better code faster.
Dart supports types, without requiring them. You can omit types when you want to move very quickly, aren’t sure what structure to take, or simply want to express something you can’t with the type system. You can add types as your program matures, the structure becomes more evident, and more developers join the project. Dart’s optional types are static type annotations that act as documentation, clearly expressing your intent. Using types means that fewer comments are required to document the code, and tools can give better warnings and error messages.
Dart scales from small scripts to large, complex apps. Web development is very much an iterative process. With the reload button acting as your compiler, building the seed of a web app is often a fun experience of writing a few functions just to experiment. As the idea grows, you can add more code and structure. Thanks to Dart’s support for top-level functions, optional types, classes, and libraries, your Dart programs can start small and grow over time. Tools such as Dart Editor help you refactor and navigate your code as it evolves.
Dart has a wide array of built-in libraries. The core library supports built-in types and other fundamental features such as collections, dates, and regular expressions. Web apps can use the HTML library—think DOM programming, but optimized for Dart. Command-line apps can use the I/O library to work with files, directories, sockets, and servers. Other libraries include URI, UTF, Crypto, Math, and Unit test.
Dart supports safe, simple concurrency with isolates. Traditional shared-memory threads are difficult to debug and can lead to deadlocks. Dart’s isolates, inspired by Erlang, provide an easier to understand model for running isolated, but concurrent, portions of your code. Spawning new isolates is cheap and fast, and no state is shared. In web apps, isolates even compile to Web workers.
Dart supports code sharing. Traditional web programming workflows can’t integrate third-party libraries from arbitrary sources or frameworks. With the Dart package manager (pub) and language features such as libraries, you can easily discover, install, and integrate code from across the web and enterprise.
Dart is open source. Dart was born for the web, and it’s available under a BSD-style license.

Example

This example includes a task that executes a finite-state-machine (FSM) periodically every 5 seconds. When in the “running” state the task generates 3 random numbers.
Redis database 5 is used to store the results (random numbers) in a hash using the hash-keys “random1”, “random2” and “random3”. The hash key is called “task1”.
The task uses messages stored in the same key “task1” and the hash-key “message”. The message options are: “goToPause”,”goToRun” and “goToEnd”.
The task states are: “paused”, ”running” and “ended”.
Start the example in a server Dart VM and use the official Redis client to send messages to the FSM.


// calceng_1.dart
// S4M - Celso Axelrud
// 12/9/2013

import "package:redis_client/redis_client.dart";
import "dart:math" as math;
import "dart:async" as async;

main() {
 
  var connectionString = "127.0.0.1:6379";

  List CalcRand(RedisClient client){
    List r=[0.0,0.0,0.0];
    var r1 = new math.Random();
    var r2 = new math.Random();
    var r3 = new math.Random();
    double r1a=r1.nextDouble();
    double r2a=r2.nextDouble();
    double r3a=r3.nextDouble();
   
   
    client.hset("task1", "random1", r1a);
    client.hset("task1", "random2", r2a);
    client.hset("task1", "random3", r3a);
    r[0]=r1a;r[1]=r2a;r[2]=r3a;
    return r;
  }


  //Connect to Redis
  RedisClient.connect(connectionString)
      .then((RedisClient client) {
       
            //Use db5
            client.select(5).then((_)=>print("selected"));
       
            List r10;
           

            //Task1 -----------------------
            String task1State='paused';
            String task1Message="";

           
            new async.Timer.periodic(new Duration(seconds:5),
                (t){
                    //r10=CalcRand(client);
                    //print('timer $t , $r10');
                   
                    //get message
                    client.hget("task1", "message")
                    .then((String reply){
                      print("Message: $reply");
                      task1Message=reply;
                    } );
                   
                    //clear message
                    client.hset("task1","message","")
                    .then((_)=>print("clear"));
                   
                    switch(task1Message)
                    {
                      case 'goToRun':
                        task1State='running';
                        break;
                      case 'goToPause':
                        task1State='paused';
                        break;
                      case 'goToEnd':
                        task1State='ended';
                        t.cancel();
                        break;
                      default:
                        break;
                    }
                    //set state
                    client.hset("task1","state",task1State)
                    .then((_)=>print("state setted: $task1State "));


                    switch(task1State)
                    {
                      case 'running':
                        r10=CalcRand(client);
                        print("calc random: $r10");
                        break;
                      default:
                        break;
                    }                   
                }
            );

          }
        )
      .catchError((e)=>print("Error 2:$e"));

}




Monday, April 8, 2013

AlgLib Examples with F#

This post presents same examples of AlgLib (http://www.alglib.net/) using F# interactive.
The examples are in the same script file.
For now, the examples include an ODE Solver and a Nonlinear System of Equations Solver.

ALGLIB is a cross-platform numerical analysis and data processing library. It supports several programming languages (C++, C#, Pascal, VBA) and several operating systems. ALGLIB features include:
  • Linear algebra (direct algorithms, EVD/SVD)
  • Solvers (linear and nonlinear)
  • Interpolation
  • Optimization
  • Fast Fourier transforms
  • Numerical integration
  • Linear and nonlinear least-squares fitting
  • Ordinary differential equations
  • Special functions
  • Statistics (descriptive statistics, hypothesis testing)
  • Data analysis (classification/regression, including neural networks)
  • Multiple precision versions of linear algebra, interpolation optimization and others algorithms (using MPFR for floating point computations)
You can find the document with all tests, script and source code at :
https://skydrive.live.com/redir?page=view&resid=BDC87EF39B001785!4127&authkey=!APhhhblU-qv1InQ

 

Sunday, March 24, 2013

Redis REST with WCF




This blog presents a Redis REST server using Microsoft Window Communication Foundation (WCF).
The servers is written in F#.
The server is tested with a Web browser HTML/javascript example.
The security features is not included yet and it will be address in future blog.
Redis for Windows is available for download from Microsoft Open Technologies Inc. at  https://github.com/MSOpenTech.. 
The Redis server was started using redis-server.exe at a command prompt window. 
The tests were performed using F# interactive inside Visual Studio Express 2012 for Web.
The Redis .NET driver used was from http://code.google.com/p/booksleeve/
You can find the document with all tests, script and source code at :
https://skydrive.live.com/#!/view.aspx?cid=BDC87EF39B001785&resid=BDC87EF39B001785%214093&app=Word

Thursday, March 21, 2013

Redis tests with F# using booksleeve driver

This document reports results related to testing Redis in Windows with F# and more specifically with F# interactive.
Redis for Windows is available for download from Microsoft Open Technologies Inc. at  https://github.com/MSOpenTech.. 
The Redis server was started using redis-server.exe at a command prompt window. 
The tests were performed using F# interactive inside Visual Studio Express 2012 for Web.
The Redis .NET driver used was from http://code.google.com/p/booksleeve/ .

The tests were based on the C# test examples from http://code.google.com/p/booksleeve/source/browse/#hg%2FTests .

You can find the document with all tests, script and source code at :
https://skydrive.live.com/#!/view.aspx?cid=BDC87EF39B001785&resid=BDC87EF39B001785%214090&app=Word

Monday, December 31, 2012

MongoDB tests with F#

 
 This post reports several results related to testing MondoDB for Windows with F# and more specifically with F# interactive 
 
MongoDB for Windows is available for download from 10gen Inc. at  http://www.mongodb.org/downloads.   
 
The MongoDB server was started using mongod.exe at a command prompt window.  
 
The tests were performed using F# interactive inside Visual Studio Express 2012 for Web 
 
The documents uses MongoDB .NET driver supported by 10gen Inc. and documented at  http://www.mongodb.org/display/DOCS/CSharp+Language+Center. 
 
The tests were based on the C# examples at http://www.mongodb.org/display/DOCS/CSharp+Driver+Tutorial. 

Friday, November 23, 2012

Using NoSQL/Redis in Windows with F#


Celso Axelrud 
11/23/2012 
 

(Also available as Skydrive MSWord at https://skydrive.live.com/#!/edit.aspx?cid=BDC87EF39B001785&resid=BDC87EF39B001785%211518&app=Word&nd=1)

 
This document reports results related to testing Redis in Windows with F#.  
 
Redis for Windows is available for download from Microsoft Open Technologies Inc. at  https://github.com/MSOpenTech.  
 
The Redis server was started using redis-server.exe at a command prompt window. 
 
The tests were performed using F# interactive inside Visual Studio Express 2012 for Web. 
 
The Redis .NET driver used was from ServiceStack (https://github.com/ServiceStack/ServiceStack.Redis ) and downloaded from https://github.com/ServiceStack/ServiceStack.Redis/downloads . 
 
The following script shows the tests:
 
// Redis - example from 
//C:\opentech\ServiceStack.Redis-master\ServiceStack.Redis-master\tests\ServiceStack.Redis.Tests\Examples\TodoApp.cs 
// System 
open System 
open System.Collections.Generic 
#r "C:\opentech\ServiceStack.Redis-v3.9.28\ServiceStack.Common.dll" 
#r "C:\opentech\ServiceStack.Redis-v3.9.28\ServiceStack.Interfaces.dll" 
#r "C:\opentech\ServiceStack.Redis-v3.9.28\ServiceStack.Text.dll" 
#r "C:\opentech\ServiceStack.Redis-v3.9.28\ServiceStack.Redis.dll" 
open ServiceStack.Common.Extensions 
open ServiceStack.Text 
open ServiceStack.Redis 
  
type Todo= {mutable Id:int64; mutable Content:string;mutable Order:int;mutable Done:bool} 
let redisClient = new RedisClient("localhost") //6379 
redisClient.FlushAll();; 
let redisTodos = redisClient.As<Todo>();; 
(*> val redisTodos : Generic.IRedisTypedClient<Todo> *) 
let todo= {Id=redisTodos.GetNextSequence();Content = "Learn Redis";Order = 1;Done=false};; 
(*> val todo : Todo = {Id = 1L; 
Content = "Learn Redis"; 
Order = 1; 
Done = false;} *) 
redisTodos.Store(todo);; 
let savedTodo = redisTodos.GetById(todo.Id);; 
(*> val savedTodo : Todo = {Id = 1L; 
Content = "Learn Redis"; 
Order = 1; 
Done = false;} *) 
let allTodos = redisTodos.GetAll();; 
assert(allTodos.Count=1);; 
[for i in allTodos -> i.Content];; 
(*> val it : string list = ["Learn Redis"] *) 
savedTodo.Done <- span="span">true;; 
redisTodos.Store(savedTodo);; 
let savedTodo2 = redisTodos.GetById(todo.Id);; 
(*> val savedTodo2 : Todo = {Id = 1L; 
Content = "Learn Redis"; 
Order = 1; 
Done = true;} *) 
redisTodos.DeleteById(savedTodo.Id);; 
let allTodos2 = redisTodos.GetAll() 
assert(allTodos2.Count=0)