Home

Documentation

Project Support

Changes in Version 2 of GraspExtrasSoftwareFitspipeSocketProtocol

Author:
crae
Timestamp:
Tue Sep 10 10:56:46 2019
Comment:
I believe this culls the worst of the newer trac markup. I now need to grab the image versions of the example sequence diagrams and attach them.

Legend:

Unmodified
Added
Removed
Modified
  • GraspExtrasSoftwareFitspipeSocketProtocol

    v1 v2
    1 1  = Grasp Software Extras: Fitspipe Network Protocol = 
    2     [[PageOutline(2-5,Contents,pullout)]] 
      2   This page describes the protocol used by the 'fitspipe' server bundled as part of the [wiki:GraspExtrasSoftwareFitspipe 'extras'] package in the [wiki:GraspSw software tarball]. It gives a general overview of the server and documents the basic commands used by clients sending or receiving image frames to/from the server. 
    2    We're being forced to document how fitspipe works below the stargrasp toolchain level for DL-NIRSP, so before it to the point where I'm happy to commit it to some more permanent format, I'm writing things up here (the plantuml stuff will likely be helpful for ladder diagrams, etc.)
      2  Emphasis is placed on clients retrieving data; it is expected that external entities will be more likely to be taking frames from a system supplied with GRASP controllers rather than putting data
    2 2  == Description == 
    3 3  ''"fitspipe"'' is a network server process that is used to transmit streams of image data from various instruments. Due to its background in astronomy, it is primarily focused on handling data that conforms to the [https://fits.gsfc.nasa.gov/fits_documentation.html FITS] format. As such, some of the conventions used are somewhat FITS-centric. 
    4 4  While it is capable of managing frames from still cameras, its original intent was distribution of video. 
    5 5  To summarise its overall purpose, fitspipe exists to: 
    6 6    * Allow producer clients to distribute image data without needing knowledge of consumers. 
    7 7    * Allow multiple consumer clients to receive image data independently from one another. 
    8 8    * Act as a frame buffer to lessen the requirement that clients always keep up with video frame rates. 
    9 9    * Keep potentially high-frame-rate, high-bandwidth streams away from disk storage. fitspipe predates the existence of fast SSDs, and not relying on disk storage avoids problems with access times, transfer rates, file management, ssd flash wear, etc. 
    10 10    * Decouple the operation of the actual instrument producing image data (protocols, etc.) from the transport of data. 
    11 11  For example, the IR cameras for DL-NIRSP transport data via UDP jumbo frames with no facility to retransmit missed packets. Since attempting to distribute data that way would be a nightmare, the camera server publishes each frame as it's received to fitspipe, translating to TCP, big buffers and normal MTUs. 
    12 12  This document is intended for those that need to interface with fitspipe at the socket level. For users that simply want to quickly use fitspipe in a system, the suggested first step is to use the command-line tools available in the STARGRASP software tarball at https://www.stargrasp.org/wiki/GraspSw . 
    13 13  == General Operational Concepts == 
    14 14  fitspipe allows multiple independent streams of data to exist simultaneously. In fitspipe parlance, these are termed ''"feeds"''. Each feed represents one or more buffered frames of image data. Each frame received is assigned an incrementing sequence number, which is used by clients to retrieve data. 
    15 15  The buffer size for a stream is a function of image dimensions and ''"depth"'' in fitspipe parlance.  The ''"depth"'' here refers to number of buffered frames that will be kept in memory (not image format or bits per pixel, of which only 16bpp is currently supported by fitspipe.)  The depth can be set on the server command line. 
    16 16  Clients sending frames of image data to the fitspipe server (''"put"'') specify which feed the data should be published to. New feeds are created on demand  
    17 17  Clients retrieving frames of image data from the server (''"get"'') are responsible for keeping track of which frames are available in the server's buffer versus which frames they have yet to receive. To assist data retrieval without polling, a blocking mechanism exists to allow a client to wait for the next frame to arrive. 
    18 18  All image data transferred from fitspipe is sent in network byte order, i.e. big-endian. 
    19 19  == Protocol Stack == 
    20    '''Basically lifted straight from the DLCam command interface doc...'''  Which lifted its content from the [https://svn.ifa.hawaii.edu/gpc/archive/psdc/gpc-conductor-interface/ PS1 OTIS-CAM interface document.] 
    21     
    22 20  Layers of network communications are often classified using the OSI Reference Model. Because the camera 
    23 21  system is not just a concept, but a physical server with hardwired connection to the observatory, it is possible to describe all layers 
    24 22  now: 
    25      ||=Layer=||=Purpose=||=Commands=||=Data=|| 
      23  ||'''Layer'''||'''Purpose'''||'''Commands'''||'''Data'''|| 
    25      ||Layer 1    ||Physical     || 10GBase-T    || 10GBase-T || 
      23  ||Layer 1    ||Physical     || 10GBase-T    || 10GBase-T || 
    26      ||Layer 2    ||Data Link    || Ethernet     || Ethernet  || 
      24  ||Layer 2    ||Data Link    || Ethernet     || Ethernet  || 
    27      ||Layer 3    ||Network      || IP           || IP        || 
      25  ||Layer 3    ||Network      || IP           || IP        || 
    28      ||Layer 4    ||Transport    || TCP          || TCP       || 
      26  ||Layer 4    ||Transport    || TCP          || TCP       || 
    29      ||Layer 5    ||Session      || Socket       || Socket    || 
      27  ||Layer 5    ||Session      || Socket       || Socket    || 
    30      ||Layer 6    ||Presentation || custom       || custom    || 
      28  ||Layer 6    ||Presentation || custom       || custom    || 
    31      ||Layer 7    ||Application  || ''cash:'' Camera Shell || Raw/FITS data || 
      29  ||Layer 7    ||Application  || ''cash:'' Camera Shell || Raw/FITS data || 
    32      ||||||||   '''Table 1: Protocol Stack Summary'''   || 
    33 30  In so many words, a “socket connection over TCP/IP on Ethernet” could describe interface layers 1-5, but to be explicit it is worth 
    34 31  describing each layer with some detail to avoid mistaken assumptions (e.g. Will we support IPv6? Jumbo packets?) 
    35 32  === Physical Layer === 
    36 33  Between the camera server running fitspipe and the rest of an observatory (CSS in the case of DKIST) will be a series of switches, fibers and/or copper wiring connecting the camera server to the facility network. 
    37 34  === Data Link Layer === 
    38 35  The camera server expects to operate over 10-Gigabit Ethernet with normal (1500 byte) max. transmission units (MTU). Operation over lower-bandwidth links is possible, but will eventually result in missing data as the aggregate data rate of the camera at the required frame rates is above 1 Gigabit/sec. Operation over higher-bandwidth links is permissible. 
    39 36  === Network Layer === 
    40 37  Internet Protocol v4 will be used. The camera server running fitspipe will be on a private IP subnet (not Internet). If necessary, switches will route communications with the facility. 
    41 38  === Transport Layer === 
    42 39  TCP will be used on top of IPv4. 
    43 40  By default, the command server will listen for new connections on TCP port 9999. However, clients should have the flexibility to 
    44 41  connect on a different port. 
    45 42  === Session Layer === 
    46 43  Clients of the camera command interface establish a session by opening a socket. The number of active sessions is limited only by 
    47 44  the operating system and CPU resources; the latter becomes increasingly important for high-resolution, high-frame-rate applications. 
    48 45  It is possible for any given client to use a new socket for every frame retrieved, but it is also acceptable to re-use a single socket connection. 
    49 46  === Presentation Layer === 
    50 47  A client need only be concerned with managing the application layer (described next). While sockets, TCP/IP and ethernet are all 
    51 48  standard, the presentation layer is the first custom layer so a description is necessary. 
    52 49  Something something line-based messages. 
    53 50  First, a “line” is defined as any string of up to 32767 ASCII characters. Within the communications stream, lines are terminated by `\r` or `\n `. Note that this does not imply that the application layer supports command line lengths of 32767. Line type prefixes and encoding of control characters and non-7-bit ASCII characters within the message itself may occupy extra room. 
    54 51  Image data is presented as binary data instead of ASCII with no encoding, FITS header included. 
    55 52  ==== Input direction (command) ==== 
    56 53  Command input is only valid when previous command response has been received completely. or a new socket has just been opened. Input consists of a maximum of 32767 characters between ASCII 32 and 127 inclusive followed by a single \r or \n (ASCII 13 or 10).  
    57 54  TBD More details on encoding. 
    58 55  ==== Output direction (response) ==== 
    59 56  Each command will return one or more lines in response. 
    60 57  After the initial command-response transaction, both the `put` and `get` commands transfer binary data in the relevant direction. 
    61 58  Each response line begins with one of the following: 
    62      ||=String=||=Description=|| 
      59  ||'''String'''||'''Description'''|| 
    62      || `'> ’`   ||Followed by echo of command, acknowledges that command was received and processing has started. || 
      59  || '> ’   ||Followed by echo of command, acknowledges that command was received and processing has started. || 
    63      || `'+ '`   ||Followed any message line (except the last) of output generated by the command. || 
      60  || '+ '   ||Followed any message line (except the last) of output generated by the command. || 
    64      || `'. '`   ||Followed by final message from command completing successfully. || 
      61  || '. '   ||Followed by final message from command completing successfully. || 
    65      || `'! '`   ||Followed by error description from command which has failed.    || 
      62  || '! '   ||Followed by error description from command which has failed.    || 
    66      ||`'# '`   ||Followed by 1-line description of image frame, then binary data. Only used in response to `get`. || 
      63  || '# '   ||Followed by 1-line description of image frame, then binary data. Only used in response to `get`. || 
    67      ||||  '''Table 2: Response line prefixes'''  || 
    68 64  The client must not feed a new line of input until ’.’ or ’!’ has been received. The client is also not expected to close the session until 
    69 65  such complete response has been received. 
    70 66  The prefix ’* ’ followed by an asynchronous notification is reserved for urgent/out of band messages not associated with any specific 
    71 67  command. For example, 
    72 68    {{{ 
    73 69  ’* warning: system shutting down in 5 minutes’ 
    74 70  }}} 
    75 71  could appear at any time (including in the midst of a response to a command, but never in a way that breaks another line.) 
    76 72  In addition to the Conductor Camera Shell namespace, the camera server command interface and the low-level interface to the FPGA boards also uses this presentation layer. 
    77 73  === Application Layer === 
    78 74  If desired, C Language bindings will be provided to interface to the presentation layer. The status server application programming 
    79 75  interface (API) is described in detail in the Status Server Client C API document. 
    80 76  TBD: A separate C library for accessing the command interface will allow the user to: 
    81 77  • Set callback to receive response message strings. 
    82 78  • Set callback to receive asynchronous messages. 
    83 79  • Check for new messages. 
    84 80  • Send an ASCII command string, wait for it to complete, and return binary (PASS or FAIL) status of the request. 
    85 81  • Interrupt the system (from another session.) 
    86 82  Summary of command string input syntax: 
    87 83  • Command names are lower case letters, digits, and underscores only. 
    88 84  • Command parameters are typically specified as a series of `name=value` pairs. 
    89 85  • Command parameters follow the command name, separated by whitespace. 
    90 86  • Parameter names are case insensitive letters, digits, and underscores only. 
    91 87  • Parameter names may be defined containing a single ’ * ’ star character indicating that the parameter name may be abbreviated beyond that point. 
    92 88    * The root portion of the parameter name (up to any ’ * ’ star character given in the definition) is required. Additional characters, up to the full name, are optional but may not be mistyped. 
    93 89    * Abbreviations are intended as convenience for manual operation modes. ''Automated clients and scripts should use the full name.'' 
    94 90  • Quotation: ’...’ and "..." are accepted. 
    95 91  • Any characters after ’ # ’ outside of quotation taken as comments. 
    96 92  • Command parameters are not sensitive to white space outside of quotation. 
    97 93  • Commands may define their parameters as positional and non-positional. 
    98 94    * Positional parameters may be supplied only as a value without the name= portion of the parameter pair. 
    99 95    * Positional parameters are defined by their order in the set of command parameters. 
    100 96    * If the first character in a parameter argument is not a legal parameter name character, or if the first non-legal character is not ’=’ (equal sign) then the entire argument is interpreted as a value for a positional parameter. 
    101 97    * Non-positional parameters are fully specified by a name=value pair. 
    102 98    * Non-positional parameters are order-independent; each pair fully specifies the name of the parameter, so the parameter may appear anywhere in the set. 
    103 99    * Positional rules are a convenience for manual operation. Automated clients and scripts should always generate parameters with the full option name and an ’=’.  
    104 100  The actual commands intended for the application layer are described in the following section. 
    105 101  == Command Set == 
    106 102  Fitspipe supports a very limited command set. There are single commands for feed discovery/interrogation, to put a frame to the server, and to get one. These are performed with `ls`, `put` and `get` accordingly. 
    107 103  === ls === 
    108 104  `ls` lists all of the available feeds, and provides some information about each one. The command serves two purposes: 
    109 105    * Discovery of available feeds, 
    110 106    * Retrieving information about each feed prior to retrieving a frame of image data. 
    111 107  The `ls` command takes no parameters. 
    112 108  If one or more feeds exist, it receives one or more lines in response, each representing a single feed. Each per-feed line begins with the continuation string documented above, followed by the following `name=value` parameter pairs: 
    113      ||=Name=||=Type=||=Description=|| 
      109  ||'''Name'''  ||'''Type'''||'''Description'''|| 
    113      ||`'feed='`  ||String    ||Name of feed.    || 
      109  ||'feed='   ||String    ||Name of feed.    || 
    114      ||`'naxis1='` ||Integer   ||Width of image frame.|| 
      110  ||'naxis1=' ||Integer   ||Width of image frame.|| 
    115      ||`'naxis2='` ||Integer   ||Height of image frame. || 
      111  ||'naxis2=' ||Integer   ||Height of image frame. || 
    116      ||`'depth='` ||Integer   ||Number of buffered frames.|| 
      112  ||'depth='  ||Integer   ||Number of buffered frames.|| 
    117      ||`'oldest='` ||Integer   ||Sequence number of oldest frame currently held in buffer.|| 
      113  ||'oldest=' ||Integer   ||Sequence number of oldest frame currently held in buffer.|| 
    118      ||`'newest='` ||Integer   ||Sequence number of most recent frame currently held in buffer.|| 
      114  ||'newest=' ||Integer   ||Sequence number of most recent frame currently held in buffer.|| 
    119 115  This is then followed by a final line indicating successful completion of the command. 
    120 116  If no feeds have been created, then there will be no lines of data ahead if the completion indication, but the command will indicate that it was processed successfully. 
    121 117  For example: 
    122 118    {{{ 
    123 119  ls 
    124 120  + feed=default naxis1=2048 naxis2=2048 depth=300 oldest=3330 newest=3629 
    125 121  . OK 
    126 122  }}} 
    127 123  The above response indicates that there is a single feed named "default" available, representing 300 buffered frames of 2048x2048-pixel image data. The oldest frame can be retrieved with sequence number 3330, and the most recent by requesting frame 3629. Frames with sequence numbers below that range have been dropped from the buffer and no longer exist, and frames with higher sequence numbers have yet to be received. 
    128 124  === put === 
    129 125  The `put` command publishes a single frame of FITS image data to the server. Given the name of a feed, the server then expects a binary transfer of a full simple FITS image, including a complete header and padding as required in a FITS file if it were on disk. 
    130 126  Because the transfer includes the header, the client doesn't need to specify any other information about the image being transferred other than the feed name; the FITS header defines the dimensions of the data, etc. This can be useful for general-purpose clients that don't need to know anything about the data they're uploading. 
    131 127  The put command takes the following parameters: 
    132      ||=Name=||=Type=||=Required?=||=Description=|| 
      128  ||'''Name'''||'''Type'''||'''Required?'''||'''Description'''|| 
    132      ||`'feed='` ||String||Required||Name of the feed to publish to. If the feed doesn't already exist, it is created.|| 
      128  ||'feed=' ||String||Required||Name of the feed to publish to. If the feed doesn't already exist, it is created.|| 
    133 129  The server replies with a single line indicating success or failure. Upon success, the client then writes the entire FITS image, header included, as binary data. 
    134 130  === get === 
    135      {{{ 
    136    %%% Should we sort out the server response so that it indicates the size of the header?  
    137    It would make the client's job a fair bit simpler... 
    138     
    139    %%% SI: I don't know, but I suspect if you go try to make this improvement you'll find out why it is a challenge.  Probably some of the info needed at the time of the response is not always there yet or something.  (I.e., the server may not always know the size yet, but it has to write something into the buffer and respond right away because of the way sockio events are processed.) 
    140    }}} 
    141     
    142 131  The `get` command retrieves a single frame of image data from the server. The client passes the name of the feed and a sequence number for the frame to be retrieved, along with an indication of whether the FITS header should also be downloaded. 
    143 132  The get command takes the following parameters: 
    144      ||=Name=||=Type=||=Required?=||=Description=|| 
      133  ||'''Name''' ||'''Type'''||'''Required?'''||'''Description'''|| 
    144      ||`'feed='`  ||String||Rerquired||Name of the feed to fetch a frame from.|| 
      133  ||'feed='  ||String ||Required||Name of the feed to fetch a frame from.|| 
    145      ||`'frame='` ||Integer||Optional||Sequence number of the frame to be retrieved. If not given, default behaviour is to fetch the most recent frame.|| 
      134  ||'frame=' ||Integer||Optional||Sequence number of the frame to be retrieved. If not given, default behaviour is to fetch the most recent frame.|| 
    146      ||`'fullheader='` ||Boolean||Optional||Indicates whether FITS header should be transferred. '1' to send FITS header, '0' to send only the image data. Default value is 0.|| 
      135  ||'fullheader=' ||Boolean||Optional||Indicates whether FITS header should be transferred. '1' to send FITS header, '0' to send only the image data. Default value is 0.|| 
    147 136  The response to the get command is a little different from all other commands in a couple of ways: 
    148 137    * If the command was parsed successfully and the requested image is available, the response will be a single line beginning with `# ` as indicated above, followed by a minimal amount of data about the image. The intent is to signify that the first line is not part of the actual data, but is more like a comment providing more information about the following image data. 
    149 138    * If the command was parsed successfully, the feed exists, but the frame number is for a future frame that does not yet exist, the response line will be started with `# `, but further output to that client will block until that frame becomes available. Once the frame has been uploaded to the server, the rest of the metadata comment line will be sent, followed by the image data. 
    150 139    * The complete response line is always a fixed 40 bytes in length, including the trailing newline. 
    151 140  The fixed-length response takes the following format: 
    152 141    {{{ 
    153 142  '# frame width x height   \n' 
    154 143  }}} 
    155 144  where the fields occupy fixed positions and have fixed widths: 
    156 145    frame:: 
    157 146    The frame sequence number, as recorded by the server (integer, 10 digits wide, chars 2-11). 
    158 147    width:: 
    159 148    The width of the image in pixels (integer, 10 digits wide, chars 13-22). 
    160 149    height:: 
    161 150    The height of the image in pixels (integer, 10 digits wide, chars 26-35). 
    162 151  There is a guaranteed space between the leading `#` and the first fields and also between each of the fields. The fields are always arranged in the fixed offsets indicated above. The line ends with three spaces and a newline to fill out the remaining 40 bytes. 
    163 152  Note that if the client requests a frame number that has been discarded (i.e. is lower than the oldest buffered frame), ''the server currently responds by sending the latest frame''. ''It is up to the client to check that the frame number received is the same as that requested, and to deal with the case where it is not.'' 
    164 153  Both this behaviour and the fixed 40-byte format stems from a previous system involving small images transferred at high rates, keeping the overhead down as far as possible in terms of bytes transferred (and thus the number of packets per frame) and latency. The fixed-width/fixed-position fields also reduce the complexity of parsing the received data, further reducing latency. 
    165 154  Only 16-bit-per-pixel image data is supported, so at this point, the size of the image data in bytes is known. 
    166 155  After the initial one-line response, the server sends the frame as binary data. 
    167 156  If a header was requested, then the client is expected to detect and handle the additional data by following the standard for FITS headers. The client will not know ahead of time the size of the header, but the rules for FITS headers make it possible to determine the extent of the header. While it is outside the scope of this document to fully describe the FITS header format, the relevant rules can be summarised as follows: 
    168 157    * FITS headers consist of one or more blocks of exactly 2880 bytes. 
    169 158    * Each block consists of 36 80-byte 'cards'. 
    170 159    * Each card begins with an 8-byte text keyword that names the card. 
    171 160    * If any keyword in a block is `END     ` ("END" followed by 5 ASCII spaces), that indicates the block is the last in the header. 
    172 161      * Any keyword in a block may indicate the end of the header. 
    173 162      * No cards containing data will be found after the `END` keyword. 
    174 163    * Alternatively, if no `END` keyword is present in a header block, the header continues, and the current block will be followed by at least one more 2880-byte block. 
    175 164  After the last 2880-byte block is received for the header, the server sends the image data. The image data is contiguous with the last header block, and consists of 16 bit-per-bixel values of the dimensions indicated in the first line of the response. The first pixel sent is (0,0), followed by the rest of the first row of pixels, followed by the next row, etc. 
    176 165  If no header is requested, then the header is skipped and the image data is sent  immediately after the one-line text response. 
    177 166  Note that it is strongly recommended that clients always take the header for each image. The overhead of receiving a few header blocks per frame versus the full image frame is usually minimal, and the headers may contain metadata that could be useful to be passed on from the camera system. 
    178 167  == Common FITS Pitfalls == 
    179 168  While they are not strictly related to the fitspipe protocol, the FITS standard does lay a few traps for the unwary. A brief recap is therefore probably worthwhile: 
    180 169  As mentioned, image data is sent in network byte order, and handily the FITS standard requires big-endian image data. It may be tempting to think that little-endian systems need to do a lot of byte-swapping to "fix" the 16-bit pixels, but this is not the case so long as the data remains in FITS format. However, the client must consider endianness if it translates the image data to some other format. 
    181 170  The FITS standard does not allow for unsigned integer values for pixels; all stored values are signed. However, raw image data is typically a series of unsigned A/D converter measurements. To get around this, a standard FITS header will include two keywords, `BZERO` and `BSCALE` which are used (as per the [https://docs.astropy.org/en/stable/io/fits/usage/image.html#scaled-data astropy docs]) to transform stored values to the original physical values as follows: 
    182 171    {{{ 
    183 172  physical value = BSCALE * (storage value) + BZERO 
    184 173  }}} 
    185 174  Because the client that published the data to fitspipe followed the FITS standard, the reverse transform was applied to the raw data before uploading, and all of the pixel values were shifted and scaled. ''If the client intends to write data out to a non-FITS format, it must transform every one of the pixels received before passing the data along.'' Failure to do so will result in odd value wrapping problems, etc. 
    186 175  In general, for most 16-bit-per-pixel systems it is somewhat safe to assume that BZERO is 32768 and BSCALE is 1.0. However, it is more correct to transfer the header and retrieve the values directly from the relevant keyword values. 
    187 176  If the client intends to write a FITS image using the supplied header and image data, please note that the FITS standard requires the image data to be padded out to the next 2880-byte boundary with zeroes. Since it is trivial to generate the required padding within the client (and probably far less expensive than transferring trailing zeroes across the network), the image data from the server does '''not''' include this padding. 
    188 177  == Examples == 
    189 178  Some examples of client-server interactions to transfer frames from fitspipe. 
    190 179  Note: trailing newline characters on each message are omitted for clarity. 
    191    === Client retrieves latest frame ===  #ExampleLatestFrame 
      180  === Client retrieves latest frame === 
    192 181  The client grabs one frame (the latest in the server's buffer) from the server with no header. 
    193 182    {{{#!PlantUml 
    194 183  @startuml 
    195 184  title Fitspipe client gets latest frame 
    196 185  participant Client 
    197 186  participant Server 
    198 187  Client->Server: 'ls' 
    199 188  activate Server #FEFECE 
    200 189  Server->Client: <size:9>'+ feed=default naxis1=2048 naxis2=2048 depth=300 oldest=3330 newest=3629\n. OK'</size> 
    201 190  deactivate Server 
    202 191  note left of Client  
    203 192  Newest frame is 3629 
    204 193  end note 
    205 194  Client -> Server: 'get feed=default framenum=3629 fullheader=0' 
    206 195  activate Server #FEFECE 
    207 196  Server -> Client: '#       3629       2048 x       2048   ' 
    208 197  note left of Client 
    209 198  Client knows to expect  
    210 199  2048x2048 2-byte pixels, 
    211 200  allocates  8388608 bytes  
    212 201  for image data. 
    213 202  end note 
    214 203  Server->Client: **Binary image transfer** 
    215 204  deactivate Server 
    216 205  @enduml 
    217 206  }}} 
    218 207  === Client retrieves latest frame (including header) === 
    219 208  The client grabs one frame (the latest in the server's buffer) from the server, preceded by a header. 
    220 209    {{{#!PlantUml 
    221 210  @startuml 
    222 211  title Fitspipe client gets latest frame 
    223 212  participant Client 
    224 213  participant Server 
    225 214  Client->Server: 'ls' 
    226 215  activate Server #FEFECE 
    227 216  Server->Client: <size:9>'+ feed=default naxis1=2048 naxis2=2048 depth=300 oldest=3330 newest=33629\n. OK'</size> 
    228 217  deactivate Server 
    229 218  note left of Client  
    230 219  Newest frame is 3629 
    231 220  end note 
    232 221  Client -> Server: 'get feed=default framenum=3629 fullheader=1' 
    233 222  activate Server #FEFECE 
    234 223  Server -> Client: '#       3629       2048 x       2048   ' 
    235 224  loop 1 or more times 
    236 225  Server -> Client: **2880-byte header block** 
    237 226  note left of Client 
    238 227  Client repeatedly  
    239 228  reads 2880-byte  
    240 229  header blocks,  
    241 230  looking for END 
    242 231  end note 
    243 232  end 
    244 233  note left of Client 
    245 234  Client knows to expect  
    246 235  2048x2048 2-byte pixels, 
    247 236  allocates  8388608 bytes  
    248 237  for image data. 
    249 238  end note 
    250 239  Server->Client: **Binary image transfer** 
    251 240  deactivate Server 
    252 241  @enduml 
    253 242  }}} 
    254 243  === Client Gets Multiple Frames === 
    255 244  A client that wishes to get multiple frames has a couple of different strategies it can use, all of which are similar to the [#ExampleLatestFrame latest frame example] above. After the initial `ls` to get a sense of the current frame sequence number, the client can attack this in a couple of ways: 
    256 245    * The client queries the server with 'ls' ahead of each and every frame. 
    257 246    * The client can maintain its own running sequence number without repeated queries of the server. 
    258 247  In either case, ''it is the client's responsibility'' to ensure that the sequence number in the initial `# ` response line matches that which was requested. 
    259 248  It is also the client's choice as to how to react to a mismatch. For example, the `fitspipe-get` example tool in the STARGRASP tarball doesn't attempt to do anything intelligent - if it falls so far behind that the server returns the latest frame instead of a frame it no longer has, `fitspipe-get` simply declares the missing frames lost forever while printing a warning, instead of trying to do something more dynamic to attempt to drop as few frames as possible. 
    260 249  ==== Client Queries Server on Each Frame ==== 
    261 250  n this example, the client makes no assumptions about which is the "current" frame, so it queries the server ahead of each transfer. 
    262 251  Note: if the client relies solely on the server's view of the latest frame without maintaining any sort of state for itself, it may be vulnerable to skipping frames if the client momentarily takes longer than a single frame period to between requests. For example, if it requests and gets frame 300, then some sort of garbage collection mechanism kicks in (or similar) and the camera keeps producing new data in the meantime, it may come back and find on the next query that the current frame is 302. If it blindly requests that frame, then it never gets frame 301 from the server, and drops that frame. 
    263 252  For that reason, it is strongly recommended that ''so long as latency is not a concern'', even if it queries the server each time the client should maintain its own counter and request sequential frames even if it falls behind. For a given application, fitspipe's buffer should have been sized appropriately to allow the client to lag for a short time and then catch up again, dropping no data. 
    264 253  On the other hand, if latency is more important than receiving and processing each and every frame (e.g. for guide video), it may be preferable to assume older frames are stale and should be skipped. As such, it is probably preferable in this case to always request the latest frame regardless of however many other frames are buffered; applications such as this might also consider running fitspipe with a very small frame buffer. 
    265 254    {{{#!PlantUml 
    266 255  @startuml 
    267 256  title Fitspipe client waits for new data 
    268 257  participant Client 
    269 258  participant Server 
    270 259  loop multiple times 
    271 260  Client->Server: 'ls' 
    272 261  activate Server #FEFECE 
    273 262  Server->Client: <size:9>'+ feed=default naxis1=2048 naxis2=2048 depth=300 oldest=n-299 newest=n</size>\n.<size:9>OK'</size> 
    274 263  deactivate Server 
    275 264  note left of Client  
    276 265  Newest frame is //n//, 
    277 266  client will wait for //n+1// 
    278 267  end note 
    279 268  Client -> Server: 'get feed=default framenum=n+1 fullheader=0' 
    280 269  activate Server #FEFECE 
    281 270  Server -> Client: '# ' **(Note: remaining 38 bytes still outstanding) ** 
    282 271    
    283 272  ... Camera eventually produces a new frame and uploads to fitspipe ... 
    284 273  Server -> Client: '     n+1       2048 x       2048   ' **(Note: line continuation) ** 
    285 274  note left of Client 
    286 275  Client knows to expect  
    287 276  2048x2048 2-byte pixels, 
    288 277  allocates  8388608 bytes  
    289 278  for image data. 
    290 279  end note 
    291 280  Server->Client: **Binary image transfer** 
    292 281  end 
    293 282  deactivate Server 
    294 283  @enduml 
    295 284  }}} 
    296 285  ==== Client Maintains its own Frame Count ==== 
    297 286  Alternatively, the client, after initially querying the server, can maintain its own frame counter and request sequential frames without checking with the server each time. 
    298 287  The client may request and receive headers on each frame; this is omitted for brevity. 
    299 288    {{{#!PlantUml 
    300 289  @startuml 
    301 290  title Fitspipe client waits for new data 
    302 291  participant Client 
    303 292  participant Server 
    304 293  Client->Server: 'ls' 
    305 294  activate Server #FEFECE 
    306 295  Server->Client: <size:9>'+ feed=default naxis1=2048 naxis2=2048 depth=300 oldest=n-299 newest=n</size>\n.<size:9>OK'</size> 
    307 296  deactivate Server 
    308 297  note left of Client  
    309 298  Newest frame is //n//, 
    310 299  client will wait for //n+1// 
    311 300  end note 
    312 301  Client -> Server: 'get feed=default framenum=n+1 fullheader=0' 
    313 302  activate Server #FEFECE 
    314 303  Server -> Client: '# ' **(Note: remaining 38 bytes still outstanding) ** 
    315 304    
    316 305  ... Camera eventually produces a new frame and uploads to fitspipe ... 
    317 306  Server -> Client: '     n+1       2048 x       2048   ' **(Note: line continuation)** 
    318 307  note left of Client 
    319 308  Client knows to expect  
    320 309  2048x2048 2-byte pixels, 
    321 310  allocates  8388608 bytes  
    322 311  for image data. 
    323 312  end note 
    324 313  Server->Client: **Binary image transfer** 
    325 314  note left of Client  
    326 315  Previous frame was //n+1//, 
    327 316  request //n+2// 
    328 317  end note 
    329 318  Client -> Server: 'get feed=default framenum=n+2 fullheader=0' 
    330 319  Server -> Client: '# ' **(Note: remaining 38 bytes still outstanding) ** 
    331 320    
    332 321  ... Camera eventually produces a new frame and uploads to fitspipe ... 
    333 322  Server -> Client: '     n+2       2048 x       2048   ' 
    334 323  Server->Client: **Binary image transfer** 
    335 324  note left of Client  
    336 325  Previous frame was //n+2//, 
    337 326  request //n+3// 
    338 327  end note 
    339 328  Client -> Server: 'get feed=default framenum=n+3 fullheader=0' 
    340 329  Server -> Client: '# ' 
    341 330    
    342 331  ... Camera eventually produces a new frame and uploads to fitspipe ... 
    343 332  Server -> Client: '     n+3       2048 x       2048   ' (Note: line continuation) 
    344 333  Server -> Client: **Binary image transfer** 
    345 334  == etc. == 
    346 335  deactivate Server 
    347 336  @enduml 
    348 337  }}}