Models.cpp

// Models.cpp
//
// Author David Barrett-Lennard
// (C)opyright Cedanet Pty Ltd 2009

@import "Ceda/cxPython/cxPython.h"
@import "Ceda/cxWorkingSetIpc/WorkingSetIPC.h"
@import "Ceda/cxOperation/IWorkingSetMachine.h"
@import "Ceda/cxPersistStore/IPersistStore.h"
@import "Ceda/cxPersistStore/pref.h"
@import "Ceda/cxObject/Object.h"
@import "Ceda/cxObject/IObjectVisitor.h"
@import "Ceda/cxObject/WCSpace.h"
@import "Ceda/cxObject/PrintReflectedVariable.h"
#include "Ceda/cxUtils/TracerUtils.h"

namespace MVectorsInModels
{
    $model+ V
    {
        ceda::int32 v1[2][3];
        ceda::int32 v2;
    };

    $struct+ X isa ceda::IPersistable :
        model
        {
            V vs[3];
            bool b;
            ceda::int8 i8;
            ceda::int16 i16;
            ceda::int32 i32;
            ceda::int64 i64;
            ceda::uint8 ui8;
            ceda::uint16 ui16;
            ceda::uint32 ui32;
            ceda::uint64 ui64;
            ceda::float32 f32;
            ceda::float64 f64;
            ceda::xvector<ceda::int32> U;
            ceda::xvector<ceda::pref<ceda::IObject> > L;
        }
    {
    };

    $function+ X* AsX(ceda::ptr<ceda::IObject> obj)
    {
        return ceda::tryqccast<X>(obj);
    }

    void Run()
    {
        ceda::TraceGroup g("MVectors in models example 2");

        PyRun_SimpleString(
            @strx
            (
                import time
                ns = rootnamespace.MVectorsInModels
                X = ns.X

                class Txn(object):
                    def __init__(self, cspace):
                        self.cspace = cspace

                    def __enter__(self):
                        self.cspace.Lock(ceda.ECSpaceLockMode.Transaction)

                    def __exit__(self, etype, value, tb):
                        if etype is not None:        # an exception has occurred
                            traceback.print_exception(etype, value, tb)

                            # We do not allow transactions to be automatically committed when exceptions are
                            # raised, it is too easy for an invalid state to be committed to the database
                            # The safest thing to do is to exit the process without comitting the transaction.
                            sys.exit(-1)
                        self.cspace.Unlock()
                    
                # Open the store
                try:
                    # todo - write a portable function to get a temp folder in all platforms
                    path = 'models.ced'
                    print `dir(ceda.EOpenMode)`
                    s = ceda.LssSettings()
                    s.flushTimeMilliSec = 500
                    s = ceda.PersistStoreSettings()
                    s.lssSettings.flushTimeMilliSec = 500
                    print 's = ' + `s`
                    print 'Opening store ' + path
                    pstore = ceda.OpenPersistStore(path, ceda.EOpenMode.OM_CREATE_ALWAYS, s)
                except RuntimeError, e:
                    print e
                    raise e
                
                # Open a PSpace
                print 'Opening pspace'
                pspace = ceda.OpenPSpace(pstore, "MyPSpace", ceda.EOpenMode.OM_CREATE_ALWAYS, None)
                if pspace is not None:
                    print 'pspace non null, value is ' + str(pspace)
                    cspace = pspace.GetCSpace()
                    if cspace is not None:
                        print 'cspace non null, value is ' + str(cspace)
                    else:
                        raise RuntimeError('cspace is null')
                else:
                    raise RuntimeError('pspace is null')

                ceda.SetThreadPSpace(pspace)
                ceda.SetThreadCSpace(cspace)

                cs = ceda.GetThreadCSpace()
        
                # Create two working sets
                print 'Opening two working sets'
                with Txn(cs):
                    ws1 = ceda.OpenWorkingSetMachine("WS1", True)
                    ws2 = ceda.OpenWorkingSetMachine("WS2", False)
                
                # Create server and client
                with Txn(cs):
                    protocolId = ceda.MakeWsipcSessionProtocolId("exPython2", 1)
                    print('protocolId = {}'.format(protocolId))
                    port = 3000
                    sessionSettings = ceda.TcpMsgSessionSettings()
                    print('Creating server listening on port ' + str(port))
                    reuse_addr = True
                    ep = ceda.CreateWsipcEndPoint(ws1, protocolId, ceda.WsipcHostInfo())
                    server = ceda.CreateTcpMsgServer(ceda.EProtocol.TCP_IPv4, port, reuse_addr, ep, sessionSettings)
                
                    hostname = "127.0.0.1"
                    print('Creating client connecting to ' + str(hostname) + ':' + str(port))
                    ep = ceda.CreateWsipcEndPoint(ws2, protocolId, ceda.WsipcHostInfo())
                    client = ceda.CreateTcpMsgClient(hostname, str(port), ep, sessionSettings)

                # Bootstrap an X under the UT root
                with Txn(cs):
                    r = ws1.GetUTRoot()

                    # Illustrate bottom up construction of a data model
                    x = X.new()
                    x.b = True
                    x.i8 = 8
                    x.i16 = 16
                    x.i32 = 32
                    x.i64 = 64
                    x.ui8 = 8
                    x.ui16 = 16
                    x.ui32 = 32
                    x.ui64 = 64
                    x.f32 = 32.2
                    x.f64 = 64.2

                    #x.vs[1].v1[0][2] = 100
                    x.vs[1].v2 = 200
                    x.U.extend(range(10,15)*2)
                    x.U.remove(11)
                
                    y = X.new()
                    #y.vs[2].v1[1][1] = 10
                    y.vs[1].v2 = 20
                    y.L.insert(0,X.new())
                    y.L.insert(1,X.new())
                    x.L.insert(0,y)
                    #todo: UTRoot is now a map, but maps arent reflected
                    #r.insert(0,x)
                    #r.Map["x"] = x
                    ceda.SetUTRootEntry(r, "x", x)
                
                # Wait long enough for convergence
                time.sleep(0.1)
                
                print 'Showing result on ws2\n'
                with Txn(cs):
                    r2 = ws2.GetUTRoot()
                    #todo: UTRoot is now a map
                    #x = r.at(0).self
                    z = ceda.GetUTRootEntry(r2, 'x')
                    print 'z = ' + `z`

                with Txn(cs):
                    a = ceda.GetUTRootEntry(r, 'x')
                    rc = a.GetReflectedClass()
                    b = ns.AsX(a)
                    #print 'b = ' + `b`
                    print 'dir(b) = ' + `dir(b)`
                    print 'b.i32 = ' + `b.i32`
                    # TODO work out why this doesnt work

                #with Txn(cs):
                #    z = ceda.GetUTRootEntry(r2, 'x')
                #    print 'z = ' + `z`
                
                ceda.CloseTcpMsgServer(server)
                ceda.CloseTcpMsgClient(client)
                with Txn(cs):
                    ws1.Close()
                    ws2.Close()
                pspace.Close()
                pstore.Close()
            ));
    }
}

/*
TODO: 20 May 2009.  This crashes with heap coruption because currently the xvector<T> implementation of
operational transform assumes that T is a Plain Old Data (POD) type.
*/
namespace VectorsOfStringsInModels
{
    $struct+ X isa ceda::IPersistable :
        model
        {
            ceda::string8 s;
            ceda::xvector<ceda::string8> L;
        }
    {
    };

    void Run()
    {
        ceda::TraceGroup g("Vectors of strings in models");

        ceda::CSpaceCreator cspace;
        ceda::CSpaceLock lock;

        PyRun_SimpleString(
            @strx
            (
                import time
                ns = rootnamespace.VectorsOfStringsInModels
                X = ns.X
                
                x = X.new()
                x.s = 'hello'
                x.L.insert(0, "hello")

                print 'x = ' + `x`
            ));
    }
}

///////////////////////////////////////////////////////////////////////////////////////////////////
/*
Lachlan's example
*/
namespace Tbu
{
    $model+ Point
    {
        ceda::int32 x;
        ceda::int32 y;
    };
    
    $model+ FreestandingModel
    {
        ceda::float32 val;
        ceda::float32 array2d[2][2];
        Point X[2][3][3];
        float32 array1d[2];
        Point px;
    };

    $struct+ Ds isa ceda::IPersistable :
        model 
        {
            FreestandingModel m;
        }
    {
        $void SetFsm( const FreestandingModel& fsm )
        {
            _model_.m = fsm;
        }
    };

    $function+ void FsmByRef(FreestandingModel& fsm)
    {
        fsm.val = 100;
    }

    $function+ void FsmByVal(FreestandingModel fsm)
    {
        fsm.val = 101;
    }

    void Run()
    {
		ceda::TraceGroup g("Tbu example");
        ceda::CSpaceCreator cspace;
        PyRun_SimpleString(
            @strx
            (
                cspace = rootnamespace.ceda.GetThreadCSpace()
                cspace.Lock( 1 )
                
                fsm = rootnamespace.Tbu.FreestandingModel()
                rootnamespace.Tbu.FsmByRef(fsm)
                print `fsm`
                
                rootnamespace.Tbu.FsmByVal(fsm)
                print `fsm`

                # Can assign a value to a member of a freestanding model
                fsm.val = 50

                # Can assign a value to an array member of a freestanding model
                fsm.array1d[0] = 30;
                fsm.array2d[0][0] = 31;
                fsm.array2d[0][1] = 21;
                print 'len = ' + `len(fsm.array2d[0])`
                print 'fsm.array2d[0][1] = ' + `fsm.array2d[0][1]`
                print 'fsm.array2d = ' + `fsm.array2d`

                p = rootnamespace.Tbu.Point()
                p.x = 1000
                p.y = 2000
                fsm.px = p

                print `fsm`
                

                ds = rootnamespace.Tbu.Ds.new()
                
                # ERROR 2: Cant assign model to a datasource member (is this to be expected?)
                # source/Ceda/cxPython/src/WrapModelStructVar.cpp(354):   ****  Assertion failure : cxAssert(0)
                #ds.m = fsm

                # This works (but is ugly)...
                ds.SetFsm(fsm)

                # ERROR 3: Cant call a function taking a reference to the model type, when passing
                # a member of a datasource.
                # TypeError: Cannot assign Tbu::FreestandingModel* from non pointer type
                #rootnamespace.Tbu.FsmByRef(ds.m)

                # ERROR 4: Cant call a function taking the model type BY VALUE, when passing
                # a member of a datasource.
                # TypeError: Type mismatch - cannot convert to type Tbu::FreestandingModel
                #rootnamespace.Tbu.FsmByVal(ds.m)

                cspace.Unlock()
            ));
    }     
} // Tbu

/*
todos for python
----------------

-   Allow for python to run the message loop, and yet still be used by the application

-   Support wrapping of maps, sets, variants

-   Reflection functions allow for default values for parameters.

-   In general allow for name=value syntax when make function calls

-   Use an extensible approach to coercions

-   Support open variants

-   Support construction of non-model members

-   Python code can implement an interface or a functor, and therefore be called from C++

-   Does python offer any counterpart to RAII?

*/

namespace Models
{
    void Run()
    {
        MVectorsInModels::Run();
        VectorsOfStringsInModels::Run();
        Tbu::Run();
    }
}