Jump to content
Sign in to follow this  
Bob Schor

Problems reading what I've written

Recommended Posts

I've been using EasyXML to write header files for a data acquisition/control package I'm developing. This package writes an "analog" data file, a "digital" (or "event") data file (including timestamps), and a header file to tie them together. In the first iteration of this package, I used the LabVIEW Config (.ini) VIs, but think XML is more flexible (hence EasyXML).

 

Initially, I used NotePad to inspect the XML file, which looked fine. Today, I coded up a VI to read it and "cast" (via Variant to Data) the returned XML into the same cluster structure used to originally write the file. This "almost" worked, but I wonder if I've "bent the rules" ...

 

I designed my header to have two parts -- an "experiment" header, consisting of a single cluster that hold "global" information, such as date/time the program started, the name of the experiment and subject, and so on. Following this, I wrote out multiple "run" headers, another cluster with such information as the run number, number of samples, stimulus parameters, etc.

 

Again, the XML looks fine. Here's a sample ... What I thought would work would be to do a single EasyXML Read using the Experiment cluster type, then do multiple EasyXML Reads using the Run cluster. In analogy with other binary reads, I expected that I would have two "run" reads that would read the two run clusters, then the third read would generate an EOF "error" that I could trap (that's how I'm reading the analog and digital data files, which are read using the "binary read" VI). However, I get an error after reading the Experiment Header (which reads just fine into its cluster) -- is it not permissible to mix XML "data types" in sequential reads? That is, if I do XMLWrite(Experiment), XMLWrite(Run), XMLWrite(Run), Close, can I not do XMLRead(Experiment), XMLRead(Experiment), XMLRead(Experiment)?

 

I suppose I could write a "wrapper" around EasyXML read that does something like the following:

 

Read a line, decide on the XML type (e.g. <Experiment_Header>)

Read subsequent lines until the end of the XML type is read (e.g. <\Experiment_Header>)

Parse XML using EasyXML Parse -- use the XML type read in Step 1 to decide which cluster to use for the parse. Return the data.

Continue the above until EOF.

 

Obviously, this is something of a chore, and I don't want to do it if this functionality is already built into EasyXML (maybe I'm just doing something silly?). On the other hand, by actually reading the XML "type" line, I could have a very flexible reader/parser, perhaps something not yet present in the package.

 

Naturally, I find these things at the beginning of a Holiday -- the Good News is now I have no excuse for not taking some time off, myself! I look forward to Enlightenment.

 

Bob Schor

 

 

<Experiment_Header>

<Software>S-Lab</Software>

<Version>0.3</Version>

<DateTime>2008-07-04T14:36:39.187-05:00</DateTime>

<Experiment_Name>Test</Experiment_Name>

<Subject_Name>Dummy</Subject_Name>

<Data_Path>S-Lab Test</Data_Path>

</Experiment_Header>

<Run_Header>

<Run_>1</Run_>

<Analog_Offset>0</Analog_Offset>

<Event_Offset>5</Event_Offset>

</Run_Header>

<Run_Header>

<Run_>0</Run_>

<Analog_Offset>0</Analog_Offset>

<Event_Offset>10020</Event_Offset>

</Run_Header>

Share this post


Link to post
Share on other sites

Hi Bob,

 

Everyone at JKI will be out of the office until Monday. I'll make sure to respond with my thoughts on this, then :o

 

Have a great holiday weekend.

 

-Jim

Share this post


Link to post
Share on other sites

Dear Jim (and other JKI stalwarts),

 

I played around with EasyXML Parse, and have decided that the "bug" is really more of a "feature". I wrote a VI that basically does the following:

1. Read the entire XML file using the Read from Text File, with "line" option turned on. This gives an array of XML lines. Close the XML file once read.

2. Pass the array of XML lines into a While loop using a shift register.

3. In the While loop, get the first line. This should be an XML header of the form <Header>.

4. Create a corresponding "close" line of the form </Header> by inserting a "/" character.

5. Use Match First String to find this "close" line (which better be there!). Increment the line number to get the size of this XML "chunk".

6. Extract the chunk using Delete from Array (with the line count from step 6). Save remainder in the "output" shift register (and for Step 11).

7. Pass the chunk into a Case statement whose selector is the first XML line from Step 3 (i.e. a line of the form "<Header>").

8. Convert the multi-line XML chunk into a single multi-line string (for EasyXML Parse) by passing it through Concatenate Strings.

9. Depending on the particular XML case, pass the string through EasyXML Parse and Variant to Data. Note that both these VIs use the same "XML Typedef" to interpret the data.

10. Pass the data out to the user. In my case, I'm using shift registers on the While loop to accumulate possible multiple instances of a particular type (in the example I submitted, there are several <Run_Header> blocks).

11. Count the "remaining XML lines" wired to the output shift register (Array Size) and wire to the Stop terminal of the While loop if =0.

 

Now, if I was really clever (and knew more about the innards of LabVIEW and its data types), I might be able to figure out how to "automate" the Case statement and the TypeDefs needed for Step 9. But given the template I outlined above, it would be really simple to adopt this code for any particular user's structure. I'll try to attach this VI, and would appreciate comments and suggestions. Note that there is no explicit "error" code, though I did include a Default case in the Case statement that should probably raise an error (because it represents an XML line with an "unrecognized" header).

 

Bob Schor

FILE_Show_Header.vi

Share this post


Link to post
Share on other sites

Hi Bob,

 

I'm still out of town, so I can only answer briefly. I'll be able to spend more time on this, tomorrow.

 

First, XML documents must have one and only one root element (according to the XML spec).

 

For example, the following can be a valid XML document:

 

 

But, this is not a valid XML document, because it has three root elements:

 

 

Second, I would avoid trying to parse XML yourself. I think that EasyXML can do everything you need to do. If you are trying to extract an XML element as a raw XML string, you can just use a string data type:

 

1.png

 

Parse_Experiment.vi

 

Hopefully this gives you some ideas about how to achieve your goals.

 

Talk to you more, tomorrow.

 

-Jim

Share this post


Link to post
Share on other sites

Jim,

Thanks for the speedy reply, and for the clear explanation of What Works, What Doesn't Work, and Why.

 

I now see that my header is really "pseudo-XML", being a concatenation of multiple XML documents, each with their own root element. The VI that I posted yesterday is able to parse this, using EasyXML Parse and some logic to pull out each of the XML "sub-documents" based on the successive root elements.

 

The reason I created the header file (which has an extension of ".hedr", rather than .XML, so it doesn't have to follow any "external rules") with multiple root elements is that I want to be able to write to it multiple times during the experiment. At the beginning of the experiment, I "know" various bits of global information, such as the date, time, name of the subject, specific experiment being run (which may also color the kinds of data and headers that follow), etc. I haven't actually started the analog data acquisition section yet, but potentially this global section could include number of A/D channels, their assignments, scale factors, sampling rate, etc.

 

I then start the experiment, taking multiple "runs" of data. Again, this is highly experiment-specific, but could include such things as the kind of stimulus, the frequency and amplitude of the stimulus, and the number of data points acquired. Depending on the final design, I might decide to make A/D channel assignment a run-dependent (instead of a "global") condition, whence I'd need to add this information here as well.

 

It seems more logical and certainly more straight-forward to write header file information at the appropriate time. Thus I write the global "Experiment" header information at the beginning of the VI (just after I open the various output data files), and write the run-specific information at the beginning of each run. The alternative of writing one big "proper" XML document is that I couldn't do the write until I knew all of the data that belongs in the header, that is, until the end of the experiment. Besides making me nervous not having header information "streamed" at the same time as the data (what if there's a crash or other glitch?), I'd need to save and carry this information along, instead of "off-loading" the information to the header file as it becomes known/defined. The drawback, as Jim notes, is that the resulting document is not a valid XML document. But with the tools that EasyXML provides, and the string/array facilities in LabVIEW, it is very simple to create a "Read/Parse Multi-XML Document" similar to the one I attached earlier.

 

Bob Schor

Share this post


Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...
Sign in to follow this  

×
×
  • Create New...

Important Information

By using this site, you agree to our Terms of Use.