001/*
002 * $Id: HDF.java,v 1.7 2011/03/24 16:06:33 davep Exp $
003 *
004 * This file is part of McIDAS-V
005 *
006 * Copyright 2007-2011
007 * Space Science and Engineering Center (SSEC)
008 * University of Wisconsin - Madison
009 * 1225 W. Dayton Street, Madison, WI 53706, USA
010 * https://www.ssec.wisc.edu/mcidas
011 * 
012 * All Rights Reserved
013 * 
014 * McIDAS-V is built on Unidata's IDV and SSEC's VisAD libraries, and
015 * some McIDAS-V source code is based on IDV and VisAD source code.  
016 * 
017 * McIDAS-V is free software; you can redistribute it and/or modify
018 * it under the terms of the GNU Lesser Public License as published by
019 * the Free Software Foundation; either version 3 of the License, or
020 * (at your option) any later version.
021 * 
022 * McIDAS-V is distributed in the hope that it will be useful,
023 * but WITHOUT ANY WARRANTY; without even the implied warranty of
024 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
025 * GNU Lesser Public License for more details.
026 * 
027 * You should have received a copy of the GNU Lesser Public License
028 * along with this program.  If not, see http://www.gnu.org/licenses.
029 */
030
031package edu.wisc.ssec.mcidasv.data.hydra;
032
033import java.io.*;
034import java.nio.*;
035import java.nio.channels.*;
036
037/**
038 * Provides access to HDF4 files via the plug_hdf executable.
039 */
040public class HDF {
041    
042    private static final int INT_SIZE = 4;
043    
044    Process process;
045    InputStream readable;
046    OutputStream writable;
047    
048    public HDF(String exe) throws IOException {
049      Runtime rt = Runtime.getRuntime();
050      process = rt.exec(exe);
051      writable = process.getOutputStream();
052      readable = process.getInputStream();
053    }
054
055    int command(int cmd_id, byte[] paramBlock) 
056      throws IOException {
057      byte[] cc = intToByteArray(cmd_id);
058      byte[] b_array = new byte[4+paramBlock.length];
059      System.arraycopy(cc,0,b_array,0,4);
060      System.arraycopy(paramBlock,0,b_array,4,paramBlock.length);
061      writable.write(b_array);
062      writable.flush();
063
064      b_array = new byte[4];
065      int n = readable.read(b_array,0,4);
066      if (n != 4) throw new IOException("problem reading return code from command: "+cmd_id);
067      int rc = byteArrayToInt(b_array);
068      return rc;
069    }
070
071    synchronized int start(String filename) throws Exception {
072      int rc = command(2, stringBlock(filename));
073      if (rc >= 0) {
074        return readStruct();
075      }
076      else {
077        throw new Exception("start failed on "+filename+", returned: "+rc);
078      }
079    }
080
081    byte[] stringBlock(String name) throws IOException {
082      int len = name.length();
083      ByteArrayOutputStream bos = new ByteArrayOutputStream();
084      DataOutputStream das = new DataOutputStream(bos);
085      das.writeInt(len);
086      byte[] int_byte = bos.toByteArray();
087      byte[] str_bytes = name.getBytes();
088      byte[] byte_array = new byte[4+len];
089      System.arraycopy(int_byte, 0, byte_array, 0, 4);
090      System.arraycopy(str_bytes, 0, byte_array, 4, len);
091      return byte_array;
092    }
093
094    byte[] intToByteArray(int a) throws IOException {
095      ByteArrayOutputStream bos = new ByteArrayOutputStream();
096      DataOutputStream das = new DataOutputStream(bos);
097      das.writeInt(a);
098      byte[] int_byte = bos.toByteArray();
099      return int_byte;
100    }
101
102    byte[] intArrayToByteArray(int[] a) throws Exception {
103      int a_len = a.length;
104      ByteArrayOutputStream bos = new ByteArrayOutputStream();
105      DataOutputStream das = new DataOutputStream(bos);
106      das.writeInt(a_len);
107      for (int k=0; k<a_len;k++) das.writeInt(a[k]);
108      byte[] b_array = bos.toByteArray();
109      return b_array;
110    }
111
112    int byteArrayToInt(byte[] b) throws IOException {
113      ByteArrayInputStream bis = new ByteArrayInputStream(b);
114      DataInputStream dis = new DataInputStream(bis);
115      int d = dis.readInt();
116      return d;
117    }
118
119    int[] byteArrayToInt(int n_elems, byte[] b) throws IOException {
120      ByteArrayInputStream bis = new ByteArrayInputStream(b);
121      DataInputStream dis = new DataInputStream(bis);
122      int[] iarray = new int[n_elems];
123      for (int k=0; k<n_elems; k++) {
124        int d = dis.readInt();
125        iarray[k] = d;
126      }
127      return iarray;
128    }
129    
130    int readStruct() throws IOException {
131      byte[] b_array = new byte[4];
132      int n = readable.read(b_array,0,4);
133      if (n != 4) throw new IOException("number of bytes read not what expected");
134      int d = byteArrayToInt(b_array);
135      return d;
136    }
137
138    String readString() throws IOException {
139      byte[] b_array = new byte[4];
140      int n = readable.read(b_array,0,4);
141      if (n != 4) throw new IOException("number of bytes read not what expected");
142      int nelem = byteArrayToInt(b_array);
143      b_array = new byte[nelem];
144      n = readable.read(b_array,0,nelem);
145      if (n != nelem) throw new IOException("number of bytes read not what expected");
146      return new String(b_array);
147    }
148
149    synchronized HDFArray readattr(int id, int attr_index) throws Exception {
150      byte[] ba = intToByteArray(id);
151      byte[] sb = intToByteArray(attr_index);
152      byte[] cb = new byte[4+sb.length];
153      System.arraycopy(ba,0,cb,0,4);
154      System.arraycopy(sb,0,cb,4,sb.length);
155
156      int rc = command(4, cb);
157      if (rc >= 0) {
158        String data_type = readString();
159        int element_count = readStruct();
160        HDFArray obj = readRawBlock(data_type, element_count);
161        return obj;
162      }
163      else {
164        throw new Exception("readattr failed on id,attr_index: "+id+","+attr_index);
165      }
166    }
167
168    synchronized int getdimid(int sds_id, int dim_index) throws Exception {
169      byte[] ba = intToByteArray(sds_id);
170      byte[] sb = intToByteArray(dim_index);
171      byte[] cb = new byte[4+sb.length];
172      System.arraycopy(ba,0,cb,0,4);
173      System.arraycopy(sb,0,cb,4,sb.length);
174
175      int rc = command(10, cb);
176      if (rc >= 0) {
177        return readStruct();
178      }
179      else {
180        throw new Exception("getdimid failed on sds_id,dim_index: "+sds_id+","+dim_index);
181      }
182    }
183
184    synchronized HDFDimension diminfo(int dim_id) throws Exception {
185      byte[] ba = intToByteArray(dim_id);
186
187      int rc = command(11, ba);
188      if (rc >= 0) {
189        String dim_name = readString();
190        int dim_size = readStruct();
191        int dim_type_code = readStruct();
192        int dim_n_attrs = readStruct();
193        return new HDFDimension(dim_name, dim_size, dim_type_code, dim_n_attrs);
194      }
195      else {
196        throw new Exception("diminfo failed on dim_id: "+dim_id+" returned: "+rc);
197      }
198
199    }
200
201    synchronized HDFVariableInfo getinfo(int sds_id) throws Exception {
202      byte[] ba = intToByteArray(sds_id);
203      int rc = command(13, ba);
204      if (rc>=0) {
205        String var_name = readString();
206        int var_rank = readStruct();
207
208        byte[] b_array = new byte[4];
209        int n = readable.read(b_array, 0, 4);
210        int n_words = byteArrayToInt(b_array);
211
212        byte[] b_array2 = new byte[n_words*4];
213        n = readable.read(b_array2, 0, b_array2.length);
214        if (n != b_array2.length) throw new Exception("msg");
215        int[] var_dim_lengths = byteArrayToInt(var_rank, b_array2);
216
217        int var_data_type = readStruct();
218        int var_num_attrs = readStruct();
219
220        return new HDFVariableInfo(var_name, var_rank, var_dim_lengths, var_data_type, var_num_attrs);
221      }
222      else {
223        throw new Exception("getinfo failed on sds_id: "+sds_id+" returned: "+rc);
224      }
225    }
226
227    synchronized HDFFileInfo fileinfo(int sd_id) throws Exception {
228      byte[] ba = intToByteArray(sd_id);
229      int rc = command(12, ba);
230      if (rc>=0) {
231        int num_datasets = readStruct();
232        int num_global_attrs = readStruct();
233        return new HDFFileInfo(num_datasets, num_global_attrs);
234      }
235      else {
236        throw new Exception("fileinfo failed on sd_id: "+sd_id+", returned: "+rc);
237      }
238    }
239
240    synchronized int select(int id, int index) throws Exception {
241      byte[] ba = intToByteArray(id);
242      byte[] sb = intToByteArray(index);
243      byte[] cb = new byte[4+sb.length];
244
245      System.arraycopy(ba,0,cb,0,4);
246      System.arraycopy(sb,0,cb,4,sb.length);
247
248      int rc = command(3, cb);
249      if (rc >= 0) {
250        return readStruct();
251      }
252      else {
253        throw new Exception("select failded on id, index: "+id+","+index+" returned: "+rc);
254      }
255    }
256
257    synchronized HDFArray readRawBlock(String data_type, int element_count) throws IOException {
258      byte[] b_array = new byte[4];
259      int n = readable.read(b_array,0,4);
260      if (n != 4) throw new IOException("msg");
261      int nbytes = byteArrayToInt(b_array);
262
263      ByteBuffer b_buf = ByteBuffer.allocate(nbytes);
264      byte[] bb = b_buf.array();
265
266      //- workaround for problem seen only on MAC OS X
267      int ntotal = 0;
268      while(ntotal < nbytes) {
269        n = readable.read(bb, ntotal, nbytes-ntotal);
270        ntotal += n;
271      }
272
273      if (data_type.equals("s")) {
274        return HDFArray.make(new String[] {new String(bb)});
275      }
276
277      // use java.nio to format bytes
278      b_buf.rewind();
279
280      if ((data_type.equals("I")) || (data_type.equals("i"))) {
281        IntBuffer buf = b_buf.asIntBuffer();
282        int[] array = new int[element_count];
283        buf.get(array);
284        return HDFArray.make(array);
285      }
286      if ((data_type.equals("H")) || (data_type.equals("h"))) {
287        ShortBuffer buf = b_buf.asShortBuffer();
288        short[] array = new short[element_count];
289        buf.get(array);
290        return HDFArray.make(array);
291      }
292      if ((data_type.equals("D")) || (data_type.equals("d"))) {
293        DoubleBuffer buf = b_buf.asDoubleBuffer();
294        double[] array = new double[element_count];
295        buf.get(array);
296        return HDFArray.make(array);
297      }
298      if ((data_type.equals("F")) || (data_type.equals("f"))) {
299        FloatBuffer buf = b_buf.asFloatBuffer();
300        float[] array = new float[element_count];
301        buf.get(array);
302        return HDFArray.make(array);
303      }
304
305      return null;
306    }
307
308    synchronized int endaccess(int sds_id) throws Exception {
309      int rc = command(7, intToByteArray(sds_id));
310      if (rc >= 0) {
311        return rc;
312      }
313      else {
314        throw new Exception("endaccess failed on sds_id: "+sds_id+" returned: "+rc);
315      }
316    }
317
318    synchronized int findattr(int id, String name) throws Exception {
319      byte[] ba = intToByteArray(id);
320      byte[] sb = stringBlock(name);
321      byte[] cb = new byte[4+sb.length];
322      System.arraycopy(ba,0,cb,0,4);
323      System.arraycopy(sb,0,cb,4,sb.length);
324 
325      int rc = command(5, cb);
326      if (rc >= 0) {
327        return readStruct();
328      }
329      else {
330        throw new Exception("findattr failed on id, name: "+id+","+name+" returned: "+rc);
331      }
332    }
333
334    synchronized int nametoindex(int id, String name) throws Exception {
335      byte[] ba = intToByteArray(id);
336      byte[] sb = stringBlock(name);
337      byte[] cb = new byte[4+sb.length];
338      System.arraycopy(ba,0,cb,0,4);
339      System.arraycopy(sb,0,cb,4,sb.length);
340
341      int rc = command(8, cb);
342      if (rc >= 0) {
343        return readStruct();
344      }
345      else {
346        throw new Exception("nametoindex failed on id,name: "+id+","+name+" returned: "+rc);
347      }
348    }
349
350    synchronized HDFArray readdata(int sds_id, int[] start, int[] stride, int[] edges) throws Exception {
351      int len = 0;
352
353      byte[] ba_id = intToByteArray(sds_id);
354      len += 4;
355      byte[] ba_start = intArrayToByteArray(start);
356      len += ba_start.length;
357      byte[] ba_stride = intArrayToByteArray(stride);
358      len += ba_stride.length;
359      byte[] ba_edges = intArrayToByteArray(edges);
360      len += ba_edges.length;
361      byte[] b_array = new byte[len];
362
363      len = 0;
364      System.arraycopy(ba_id,0,b_array,len,ba_id.length);
365      len += ba_id.length;
366      System.arraycopy(ba_start,0,b_array,len,ba_start.length);
367      len += ba_start.length;
368      System.arraycopy(ba_stride,0,b_array,len,ba_stride.length);
369      len += ba_stride.length;
370      System.arraycopy(ba_edges,0,b_array,len,ba_edges.length);
371
372      int rc = command(6, b_array);
373      if (rc >= 0) {
374        String data_type = readString();
375        int elementCount = readStruct();
376        return readRawBlock(data_type, elementCount);
377      }
378      else {
379        throw new Exception("readdata failed on sds_id: "+sds_id+" returned: "+rc);
380      }
381    }
382
383    synchronized int vStart(int f_id) throws Exception {
384      byte[] ba = intToByteArray(f_id);
385      int rc = command(16, ba);
386      if (rc >= 0) {
387        return readStruct();
388      }
389      else {
390        throw new Exception("vStart failed on "+f_id+", returned: "+rc);
391      }
392    }
393
394    synchronized int vEnd(int v_id) throws Exception {
395      byte[] ba = intToByteArray(v_id);
396      int rc = command(18, ba);
397      if (rc >= 0) {
398        return readStruct();
399      }
400      else {
401        throw new Exception("vEnd failed on "+v_id+", returned: "+rc);
402      }
403    }
404    
405    synchronized int hOpen(String filename) throws Exception {
406      int rc = command(14, stringBlock(filename));
407      if (rc >= 0) {
408        return readStruct();
409      }
410      else {
411        throw new Exception("hopen failed on "+filename+", returned: "+rc);
412      }
413    }
414
415    synchronized int hClose(int f_id) throws Exception {
416      byte[] ba = intToByteArray(f_id);
417      int rc = command(15, ba);
418      if (rc >= 0) {
419        return readStruct();
420      }
421      else {
422        throw new Exception("h_close failed on "+f_id+", returned: "+rc);
423      }
424    }
425
426    synchronized int vsFind(int f_id, String data_name) throws Exception {
427      byte[] ba = intToByteArray(f_id);
428      byte[] sb = stringBlock(data_name);
429      byte[] cb = new byte[4+sb.length];
430      System.arraycopy(ba,0,cb,0,4);
431      System.arraycopy(sb,0,cb,4,sb.length);
432
433      int rc = command(17, cb);
434      if (rc >= 0) {
435        return readStruct();
436      }
437      else {
438        throw new Exception("vsFind failed on "+f_id+",data_name: "+data_name+", returned: "+rc);
439      }
440    }
441
442    synchronized int vsAttach(int f_id, int v_id) throws Exception {
443      byte[] ba = intToByteArray(f_id);
444      byte[] sb = intToByteArray(v_id);
445      byte[] cb = new byte[4+sb.length];
446
447      System.arraycopy(ba,0,cb,0,4);
448      System.arraycopy(sb,0,cb,4,sb.length);
449      int rc = command(18, cb);
450      if (rc >= 0) {
451        return readStruct();
452      }
453      else {
454        throw new Exception("vsAttach failed on "+f_id+",v_id: "+v_id+", returned: "+rc);
455      }
456    }
457    
458    synchronized int vsDetach(int v_id) throws Exception {
459      byte[] ba = intToByteArray(v_id);
460      int rc = command(21, ba);
461      if (rc >= 0) {
462        return readStruct();
463      }
464      else {
465        throw new Exception("vsDetach failed on "+v_id+", returned: "+rc);
466      }
467    }
468
469    synchronized HDFArray vsRead(int v_id, String name, int start_idx, int nrecs, int stride) throws Exception {
470      int len = 0;
471      byte[] ba = intToByteArray(v_id);
472      len += 4;
473      byte[] sb = stringBlock(name);
474      len += sb.length;
475      byte[] st = intToByteArray(start_idx);
476      len += 4;
477      byte[] nr = intToByteArray(nrecs);
478      len += 4;
479      byte[] se = intToByteArray(stride);
480      len += 4;
481      
482      byte[] cb = new byte[len];
483      len = 0;
484      System.arraycopy(ba, 0, cb, len, ba.length);
485      len += ba.length;
486      System.arraycopy(sb, 0, cb, len, sb.length);
487      len += sb.length;
488      System.arraycopy(st, 0, cb, len, st.length);
489      len += st.length;
490      System.arraycopy(nr, 0, cb, len, nr.length);
491      len += nr.length;
492      System.arraycopy(se, 0, cb, len, se.length);
493
494      int rc = command(19, cb);
495      if (rc >= 0) {
496        String data_type = readString();
497        int elementCount = readStruct();
498        return readRawBlock(data_type, elementCount);
499      }
500      else {
501        throw new Exception("vsRead failed on "+v_id+",name: "+name+",(start,nrecs,stride):"+start_idx+" "+nrecs+" "+stride+", returned: "+rc);
502      }
503    }
504    
505    synchronized public void close() throws IOException {
506        process.destroy();
507        readable.close();
508        writable.close();
509    }
510
511 }