Velox aborts experiment due to failure to acquire resource in DigitalMicrograph with custom code running

34 views Asked by At

We have a Talos TEM that sends data from Velox over to GMS/DigitalMicrograph during EELS Tomography experiments. During the experiment, DigitalMicrograph opens a new workspace for each collection/angle.

We would like to automatically save these workspaces as they are collected during the experiment (the system has instability issues with so many workspaces being opened).

However, my implementation of this workspace writer causes Velox to abort the acquisition after 1-2 collections with the following error (on the Velox side):

Scan was stopped

Error creating Gatan Eels interface: Resource temporarily unavailable

I suspect there is a resource lock that both Velox and my workspace-writing code are competing for, and if Velox is unable to acquire the lock (because my code is actively saving a previous workspace), it aborts the acquisition. I have tried to run my code in separate threads, but the issue remains.

The main cause of the abort is a call WorkspaceSaveAs(), even if run from a separate thread the acquisition aborts in 1-2 collections. Removing this call will allow the experiment to continue (but no data saved of course).

Any possible solutions or insight to correct the problem?

Below I have provided a skeleton of the code to try and illustrate the process being used.

// Queue new workspaces to be saved
Object ws_queue = NewMessageQueue()

// Signals to enable/disable workspace watching
Object start_signal = NewSignal(false)
Object abort_signal = NewSignal(false)


interface Events {
    void emit(Object event_source, number event_flag, Object listener);
}

Object event_map = NewEventMap( "event_map" )
event_map.AddEvent(1, "start", "void method(ScriptObject listener)" )
event_map.AddEvent(2, "stop", "void method(ScriptObject listener)" )

Object event_src_start = NewEventSource(event_map, "emit")
Object event_src_stop = NewEventSource(event_map, "emit")

string glob_root_dir = ""


// Container object with workspace information needed for writing, instances of
// this class representing 1 workspace are sent to the queue
class WorkspaceInfo : Object {
    number id
    string name
    string root_dir
    
    Object init(Object self) {...}
    
    number getIdent(Object self) {...}
    string getName(Object self) {...}
    string getRootDir(Object self) {...}    
    void setName(Object self, string ws_name) {...}
    void setIdent(Object self, number ws_id) {...}
    void setRootDir(Object self, string path) {...}
}


// Managing thread responsible for watching the queue and writing new workspaces
class IOWorker : Thread {

    void RunThread(Object self) {
        while (true) {
            Object next_ws = ws_queue.WaitOnMessage(infinity(), null)
            ...
            processWorkspace(id, name, root_dir)
            ...
            }
        }
    }
    
    // Write an actual workspace to a given location
    void processWorkspace(number ws_id, string ws_name, string root_dir) {
        ...
        workspaceSaveAs(ws_id, save_path) // <------------------- This causes the abort -----------------------
        ...
    }
}

// Managing thread responsible for watching for new workspaces and sending to the queue
class WorkspaceHandler : Thread {

    void handle_start(Object self) {
        watcher_id = WorkspaceAddWorkspaceListener(self, "workspace_added:onWorkspaceAdded") 
    }
    
    void handle_stop(Object self) {
        WorkspaceRemoveWorkspaceListener(watcher_id)
    }
    
    void setRootDir(Object self, string root_path) {...}
    
    void setStartListener(object self, object event_src) {
        event_src.AddEventListener(self, "start:handle_start")
    }
    
    void setStopListener(object self, object event_src) {
        event_src.AddEventListener(self, "stop:handle_stop")
    }
    
    void RunThread(Object self) {
        return
    }

    // Post new workspace information to queue
    void onWorkspaceAdded(Object self, number ws_id) {
        ws_queue.postMessage(ws_info)
    }
}


// Dialog to start/stop and setting output directory
class WatcherDialog : uiframe {
    ...
}
 
// Entry point
void main() {
    Object watcher = alloc(WorkspaceHandler).init()

    watcher.setStartListener(event_src_start)
    watcher.setStopListener(event_src_stop)
    
    watcher.StartThread()
    
    Object io_worker = alloc(IOWorker)
    io_worker.StartThread()
    
    object dialogObj = alloc(WatcherDialog).init()
    dialogObj.display("Auto Workspace Processor")
}

main()
0

There are 0 answers