aboutsummaryrefslogtreecommitdiff
path: root/julia/CrystFEL/src/streams.jl
blob: ce75c90663397269144b804171e1d930fde716d8 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
module Streams

import ..CrystFEL: libcrystfel
import ..CrystFEL.DataTemplates: DataTemplate, InternalDataTemplate
import ..CrystFEL.Images: Image, InternalImage
export Stream, chunkwrite, chunkread, allcrystals

# Represents the real C-side (opaque) structure.
mutable struct InternalStream end

# The Julia-side structure, needed to house the pointer to the C structure
mutable struct Stream
    internalptr::Ptr{InternalStream}
end


"""
    Stream(filename, "w", dtempl)

Opens a CrystFEL stream for writing.  Note that you must provide a `DataTemplate`,
which is needed to translate "panel coordinates" to "file coordinates".

Corresponds to CrystFEL C API routine `stream_open_for_write`.
"""
function Stream(filename, mode::AbstractString, dtempl::DataTemplate)

    if mode == "w"
        out = @ccall libcrystfel.stream_open_for_write(filename::Cstring,
                               dtempl.internalptr::Ptr{InternalDataTemplate})::Ptr{InternalStream}
        if out == C_NULL
            throw(ErrorException("Failed to open stream for reading"))
        end

        @ccall libcrystfel.stream_write_data_template(out::Ptr{InternalStream},
                               dtempl.internalptr::Ptr{InternalDataTemplate})::Cvoid

        finalizer(close, Stream(out))

    elseif mode =="r"
        throw(ArgumentError("To open a stream for reading, don't provide the DataTemplate"))

    else
        throw(ArgumentError("Unrecognised CrystFEL stream mode"*mode))
    end
end


"""
    Stream(filename, "r")

Opens a CrystFEL stream for reading.

Close the stream with `close` when you've finished (this will happen
automatically when the `Stream` object is finalized).

Corresponds to CrystFEL C API routine `stream_open_for_read`.
"""
function Stream(filename, mode::AbstractString)

    if mode == "r"
        out = @ccall libcrystfel.stream_open_for_read(filename::Cstring)::Ptr{InternalStream}
        if out == C_NULL
            throw(ErrorException("Failed to open stream for reading"))
        end
        finalizer(close, Stream(out))

    elseif mode == "w"
        throw(ArgumentError("To open a stream for writing, you must provide "
                            *"a DataTemplate: use Stream(filename, \"w\", dtempl)"))

    else
        throw(ArgumentError("Unrecognised CrystFEL stream mode"*mode))
    end
end


function Base.close(st::Stream)
    if st.internalptr != C_NULL
        @ccall libcrystfel.stream_close(st.internalptr::Ptr{InternalStream})::Cvoid
        st.internalptr = C_NULL
    end
end


function streamflags(peaks, reflections, imagedata)
    flags = 0
    if reflections
        flags |= 2
    end
    if peaks
        flags |= 4
    end
    if imagedata
        flags |= 8
    end
    return flags
end


function chunkwrite(st::Stream, image::Image; peaks=true, reflections=true)
    st.internalptr == C_NULL && throw(ErrorException("Stream is closed"))
    flags = streamflags(peaks, reflections, false)
    @ccall libcrystfel.stream_write_chunk(st.internalptr::Ptr{InternalStream},
                                          image.internalptr::Ptr{InternalImage},
                                          flags::Cint)::Cvoid
end


function chunkread(st::Stream; peaks=true, reflections=true, datageom=true)

    st.internalptr == C_NULL && throw(ErrorException("Stream is closed"))

    flags = streamflags(peaks, reflections, datageom)
    out = @ccall libcrystfel.stream_read_chunk(st.internalptr::Ptr{InternalStream},
                                               flags::Cint)::Ptr{InternalImage}
    out == C_NULL && return nothing

    finalizer(Image(out, nothing, [], [])) do x
        ccall((:image_free, libcrystfel), Cvoid, (Ptr{InternalImage},), x.internalptr)
    end

end


function allcrystals(st)
    Channel() do ch
        while true
            image = chunkread(st, peaks=false, reflections=true, datageom=false)
            image === nothing && break
            for cr in image.crystals
                put!(ch, (cr.crystal, cr.reflections))
            end
        end
    end
end


end  # of module