This page will quickly describe how to go about writing another file
translator. This is meant as a guide only. If you run into trouble,
remember to read the code to the file translators already provided,
I'd suggest you start byreading Universal.cpp and Universal.h; they
are the simplest and easiest.
Concepts:
All files are immediately stored in the UdcsChannel struct (see
Udcs and UdcsChannel Class documentation)
as soon as they are loaded. The UDCS file format, as well as the
UdcsChannel class, are very lenient as to what information they
allow themselves to hold, and this is done purposely to allow
for the most flexible and versatile file format we could come
up with. In any case, the best thing you can do when you're writing
a file translator is to try to fill out all variables as best
you can, and try to make your UdcsChannel as easy as possible
to understand for the export functions for other file formats.
Hooking things up:
Alright, before we do anything else, we need to edit Udcs.h and Udcs.cpp
in the Udcs\ directory. Towards the top of the Udcs.h file, you'll see:
const string UdcsFileTypeNames[] = ...
You'll need to add the name of the file translator you are going
to write somewhere between "Udcs" and "NULL", then
add the extension of the files you want to translate in
const string UdcsFileTypeExts[] = ...
in a position that respects the order of the first array of strings
(UdcsFileTypeNames []).
At the very top of Udcs.h, add an #include statement for the header
file of your file tranlator.
Now look at the bottom of Udcs.cpp, in the method Udcs::read(...) .
In the first, add this:
else if ( filename.substr(filename.find_last_of('.'), filename.size())
== ".[YOUR_TRANSLATOR_EXT]") {
pd_set_text(string("Importing [YOUR_TRANSLATOR] file..."));
Udcs* ret = new [YOUR_TRANSLATOR]();
delete this;
return ret->read(filename);
}
before "throw UdcsException(string("Unrecognized file
extension, cannot continue.")); ", and replace [YOUR_TRANSLATOR]
by the name of the class of your file translator, and [YOUR_TRANSLATOR_EXT]
by the extension that your file format will use.
Finally, look at Udcs::toFile(...) and add:
else if (extension == ".[YOUR_TRANSLATOR_EXT]" ||
extension == ".[YOUR_TRANSLATOR_EXT_IN_ALLCAPS]") {
pd_set_text(string("Writing [YOUR_TRANSLATOR] file..."));
[YOUR_TRANSLATOR] t(*this);
t.toFile(fd);
while(!t.file.empty())
t.file.pop_front();
}
before:
else {
throw UdcsException(string("Unrecognized file extension, Cannot
continue."));
}
replacing [YOUR_TRANSLATOR] with the name of the class
of your file translator, and [YOUR_TRANSLATOR_EXT] with
the extension that your file format will use.
Your file translator class:
Let's look at parts of Universal.h to get started:
#ifndef UNIVERSAL_H
#define UNIVERSAL_H
#include "stdafx.h"
#include "Udcs.h"
class Universal : public Udcs {
public:
Universal();
Universal(Udcs& a);
Udcs* read(string& filename);
void toFile(FILE* fd);
};
#endif
Implementing the constructor and destructor to do the things listed
below is essential for every file translator, otherwise it simply won't
work correctly:
Universal::Universal() {
this->file_type = "Universal";
}
Universal::Universal(Udcs& a) {
for (list<UdcsChannel*>::iterator t = a.file.begin(); t != a.file.end();
++t) {
this->file.push_back((*t));
}
this->file_type = a.file_type;
this->file_name = a.file_name;
}
Reading/Importing a file:
Alright, now that we have the basics covered, this is what you need
to do in the read() method of your file translator class:
- open the file described by the filename parameter. This will be
your input file, and you should expect it to have the extension that
you indicated at the top of Udcs.h
- copy filename to this->file_name and assign the name of your
file format to this->file_type.
Now we're ready to read in channels. Depending on the file format you're
dealing with, you'll be reading in only one channel for each file
(easy/fun -- this is the Embla file format), several channels
sequentially (medium/enjoyable -- this is Udcs file format), several
channel ssynchronously (hard/challenging -- this is VitalView),
several channels asynchronously (very hard/painful -- this is
ClockLab). For each channel you'll read in, you need to dynamically
allocate a UdcsChannel instance, dynamically allocate a UdcsChannel.valid_map
array, fill everything in (see UdcsChannel
documentation), and finally push its pointer to the back of
the Udcs.file list. In the very end, you must "return
this; " to signal a successful import.
Tricks and Pitfalls:
You have to use temporary files to store your data in. This is to make
sure you're not loading masses of data into RAM, and either slowing
the computer down, or getting booted from the process table because
the computer ran out of RAM. Also, you need to use the fopen, fwrite,
fread, fclose, fseek, ftell, etc. set of functions to deal with the
files, so that other file translators will be able to deal with the
temporary file that you prepared. When importing a file, you need to
have a little buffer allocated (consider using the DATA_BUFFER_SIZE
constant, which is 8192), so that you can read in a bunch of data, and
then write it out to your temp file, while doing any necessary translation
in between. Remember that the temp file can only contain raw data values
as described by this UdcsChannel.data_type flag that you set, because
that's what will be expected by other translators. Also: make sure
you rewind your files after you use them (ie. fseek(fd, 0L,
SEEK_SET); ).
If you run into file reading or opening or anything else errors, you
can simply bail out by throwing a UdcsException:
throw UdcsException(string("error text"));
This will display an Error Dialog containing the "error text",
and make the execution thread wait for the next command in the top most
dialog, the main channel list view. All UdcsChannels you have read in
will not show up in the channel list view, and you should also use a
"delete this; " statement before throwing an exception,
to prevent memory leaks.
Writing/Exporting a file:
The methods to write a file format are completely dependant on the file
format you're trying to write, so we can't really give you a set of
instructions here. Yet, there's one thing you always have to look out
for: You could come accross any data_type, unit, scale, etc, when writing
a file, so don't make ANY assumption on what you're going to get, and
prepare to either process all possible channels, or throw UdcsExceptions,
informing the user that you refuse to process a certain type of channel.
Also you cannot assume that just because the first channel was of one
type, that all the others are going to be of the same type
Enjoy expanding Udicus and Good Luck!!
|