area detector drivers towards a pattern jon thompson

21
Area Detector Drivers Towards A Pattern Jon Thompson

Upload: gabriella-barton

Post on 04-Jan-2016

218 views

Category:

Documents


0 download

TRANSCRIPT

Page 1: Area Detector Drivers Towards A Pattern Jon Thompson

Area Detector DriversTowards A Pattern

Jon Thompson

Page 2: Area Detector Drivers Towards A Pattern Jon Thompson

Contents

1. Original Practice2. Exception Handling

1. Mutexes2. NDArrays

3. ASYN Parameters4. Stream I/O5. Decomposition6. State Machines7. Vendor Library Interfacing

Page 3: Area Detector Drivers Towards A Pattern Jon Thompson

Typical Requirements

• Vendor SDK library (often a Windows DLL).• Ability to switch to a simulation library for

offline testing that is as complete as possible.• Multithreaded calls to the SDK often not

supported or just not mentioned.• Logging of SDK calls for debugging/blame

purposes.

Page 4: Area Detector Drivers Towards A Pattern Jon Thompson

Original Pattern

• It’s straightforward and simple to understand!

ADDriver

DllApi

Pco

Page 5: Area Detector Drivers Towards A Pattern Jon Thompson

Things I Would Like to Improve

• Fault propagation and reporting.• One class does everything and gets too large.• Proliferation of flags that control operation.• Keeping track of the Mutex Lock.• Be more OO and C++.• Conformance to the area detector defined

API.

Page 6: Area Detector Drivers Towards A Pattern Jon Thompson

Exceptions• To be able to throw exceptions, functions need to be

made tolerant.• C++ guarantees the execution of destructors as the stack

is unwound during an exception throw.• It should not be necessary to catch exceptions so that

clean up can be performed before re-throwing them. Note lack of ‘finally’ clause in C++.

• Use objects to represent access to resources that need to be cleaned up and take advantage of the scoping rules.

• We need smart pointer variants for mutex locks, NDArray pointers, etc.

Page 7: Area Detector Drivers Towards A Pattern Jon Thompson

The Port Driver Mutex

AnADDriver::someFunctionThatUsesTheLock(){ TakeLock takeLock(this); // Code that does stuff with the lock on aFunctionThatNeedsTheLock(takeLock);}

AnADDriver::aFunctionThatNeedsTheLock(TakeLock& takeLock){ // Do stuff with the lock { FreeLock freeLock(takeLock); // Do stuff with the lock freed } // Do more stuff with the lock taken}

• Example use:

asynPortDriverTakeLockFreeLock

Page 8: Area Detector Drivers Towards A Pattern Jon Thompson

TakeLock classclass TakeLock {friend class FreeLock;public: TakeLock(asynPortDriver* driver, bool alreadyTaken=false); TakeLock(FreeLock& freeLock); ~TakeLock();private: TakeLock(); TakeLock(const TakeLock& other); TakeLock& operator=(const TakeLock& other); asynPortDriver* driver; bool initiallyTaken;};

• Functions that require the lock to be taken should have a TakeLock object in their signature, even if they don’t use it internally.

• Copy constructor and assignment operator declared private. This forces them to be passed around by reference.

• Destructor automatically calls callParamCallbacks?

Page 9: Area Detector Drivers Towards A Pattern Jon Thompson

FreeLock class

• Functions that must be called without the lock taken could have a FreeLock object in the signature.

• Copy constructor and assignment operator declared private. This forces them to be passed around by reference.

class FreeLock {friend class TakeLock;public: FreeLock(TakeLock& takeLock); ~FreeLock();private: FreeLock(); FreeLock(const FreeLock& other); FreeLock& operator=(const FreeLock& other); asynPortDriver* driver;};

Page 10: Area Detector Drivers Towards A Pattern Jon Thompson

NDArray Pointers

• Another smart pointer type class must be used to hold NDArray pointers.

• Automatically calls release in the destructor.• Every time the array is copied, reserve is called.

class NdArrayRef {public:

NdArrayRef();NdArrayRef(NDArray* array);NdArrayRef(const NdArrayRef& other);virtual ~NdArrayRef();NdArrayRef& operator=(const NdArrayRef& other);operator NDArray*() const;

private:NDArray* array;

};

Page 11: Area Detector Drivers Towards A Pattern Jon Thompson

Asyn Parameters• We currently write this kind of code:

{ TakeLock takeLock(this); param2 = param2 + param1;}

{ lock(); int p1, p2; getIntegerParam(handle1, &p1); getIntegerParam(handle2, &p2); setIntegerParam(handle2, p2+p1); callParamCallbacks(); unlock();}

• We could write this (assuming paramx are objects):

• Or maybe this (which makes the locking requirement explicit):{ TakeLock takeLock(this); param2.set(takeLock, param2.get(takeLock) + param1.get(takeLock));}

Page 12: Area Detector Drivers Towards A Pattern Jon Thompson

Asyn Parameters• A change notification mechanism is required. We could use

templated functors to call a member function:

class MyDriver: public ADDriver{protected: IntegerParam param1; void onP1Change();};

MyDriver::MyDriver() : IntegerParam param1(this, “PARAM1”, new Notify<MyDriver>(this, &MyDriver::onP1Change)){}

void MyDriver::onP1Change(){ // Do what we need to do when P1 changes}

Page 13: Area Detector Drivers Towards A Pattern Jon Thompson

Asyn Parameters• Need an abstract class so that the system can manipulate the functors

without knowing the type of the target.

class AbstractNotify{public: AbstractNotify() {} virtual void operator()() = 0;};

template<class Target>class Notify: public AbstractNotify{public: Notify(Target* target, void (Target::*fn)()) : target(target), fn(fn) {} virtual void operator()() {(target->*fn)();}private: Target* target; void (Target::*fn)();};

Page 14: Area Detector Drivers Towards A Pattern Jon Thompson

Use stream I/O?• Wrap asynTrace inside an iostream object.• Allows use of << operators to output trace information.• This does mean that the strings are generated BEFORE the decision

whether to output or not is made.• Uses a mutex to prevent traces from different threads getting mixed up.• No comment is made on the desirability of the << operator syntax.

{ TraceStream tracer(getAsynUser(), 0x0100); tracer << “Hello world: ”; for(int i=0; i<5; i++) { tracer << i << “ “; } tracer << std::endl;}

ostream

TraceBuf

streambuf

TraceStream

-mutex: epicsMutexasynUser

Page 15: Area Detector Drivers Towards A Pattern Jon Thompson

Main Class Too Big• For many cameras, the main ADDriver derived class gets very

large.• Indicative that the decomposition of the problem could be

better.• First attempt not too successful as the three resulting classes

were too closely coupled.ADDriver ADDriver

ControllerGrabber ADComponent

ADDriver

ADDriverExPixium

Pco2Component

Pco2Pco

* *

Page 16: Area Detector Drivers Towards A Pattern Jon Thompson

State Machines• Do you end up with a collection of booleans controlling the operation of your

software?• Instead, use a state variable and draw the state transition diagram.• Let’s define a class to represent state variables.

• The version above implements a thread in which events can be processed.• The doTransition virtual function is where the state machine implementation sits.

ADDriver

User

+doTransition()

epicsThreadRunable

Pco

+doTransition()

«diagrams»

StateDiagram

StateMachine

+post(event)

epicsMessageQueue

requestQueue

stateMachine

Page 17: Area Detector Drivers Towards A Pattern Jon Thompson

State Machines• Attempting to write code

that obviously represents a state diagram

int MyDriver::doTransition(StateMachine* sm, int state, int event){ switch(state) { case STATE1: if(event == EVENT1) { doStuff(); state = STATE2; } break; case STATE2: if(event == EVENT1) { if(withCond() == 0) { state = STATE1; } else { state = STATE2; } } break; // etc... } return state;}

STATE2STATE1EVENT1()

/ result = withCond()

EVENT1()/ doStuff()

[`result == 1`]

[`result == 0`]

Page 18: Area Detector Drivers Towards A Pattern Jon Thompson

State Machines• State transitions are all written in one place.• It is a little clumsy though.• What would we like to write?class Pco{public: Pco(); enum state_t {STATE1, STATE2}; enum event_t {EVENT1}; int doStuff(); int withCond(); StateMachine sm;};

Pco::Pco(){ // curState, event, action function, next States sm.transition(STATE1, EVENT1, new Act<Sm>(this, &Sm::doStuff), STATE2); sm.transition(STATE2, EVENT1, new Act<Sm>(this, &Sm::withCond), STATE1, STATE2); // etc...}

STATE2STATE1EVENT1()

/ result = withCond()

EVENT1()/ doStuff()

[`result == 1`]

[`result == 0`]

Page 19: Area Detector Drivers Towards A Pattern Jon Thompson

Vendor Library Interface

• Abstract base class to control access to the device.• Place to implement logging and convert return codes into exceptions.• Derived classes for vendor library and simulation.• Can isolate any OS specific things inside the vendor library derived class.• Startup script creates the appropriate object.• The intention is not to abstract the API.• It can be a lot of typing though…

ADDriver

DllApi

Pco

Page 20: Area Detector Drivers Towards A Pattern Jon Thompson

Conclusions

• An extended interface library to the existing area detector is emerging.

• Along with a usage pattern.• Some things may be appropriate for integration

into a future area detector.• Some things are just my own personal hobby

horses.• C++11 could improve the notation in some places.• Any questions?

Page 21: Area Detector Drivers Towards A Pattern Jon Thompson

File Operations Mixer ClassAll file handling related parts of asynNDArrayDriver are moved out into a separate class. The constructor of this class must take an asynNDArrayDriver pointer so that it can create the asyn parameters.Any class that wants to make use of file operations includes the file operations class in its base class list.The slightly awkward bit is that this class must call the file operations class writeOctet function from its own override of writeOctet (parameter objects with a notify system would eliminate this).That’s it. All features of the file operations class are now available to the user class in just the same way as they are now.

fileOperations

-NDFilePath

-NDFilePathExists

-NDFileName

-NDFileNumber

-NDFileTemplate

-NDAutoIncrement

-NDFullFileName

-NDFileFormat

-NDAutoSave

-NDWriteFile

-NDReadFile

-NDFileWriteMode

-NDFileWriteStatus

-NDFileWriteMessage

-NDFileNumCapture

-NDFileNumCaptured

-NDFileDeleteDriverFile

+checkPath()

+createFileName(maxChars, fullFileName)

+createFileName(maxChars, filePath, fileName)

+writeOctet()

ADDriver NDPluginDriver

asynNDArrayDriver

+writeOctet()

NDPluginFile

+writeOctet()

pilatusDetector

+writeOctet()