OK, I have a winner. As Berti says, maybe I could do it with scripts from CX-Supervisor itself. The problem is that I would need a complete installation of CX-Supervisor to do that, and then I should learn the scripting language. Plus, the data extraction is just the start. I need to do a lot more processing once I have got the data, and I want to do it in Python. So, the integration will be easier if the whole thing is done in Python.
As I said, DLVs are in the "Composite Document File V2" format. The file contains several so-called streams, each of which is one of the variables you can plot in Data Log Viewer. I first used oletools to examine the data. It's a set of small programs that can read these files. One of them is olebrowse, which gives you a simple GUI that allows you to read the file, see its structure and extract streams from it. However, oletools is oriented to computer security analysis, so it's not the best tool for me.
Then you have oledump.py, which fits the job perfectly. It's a tool written in Python by Didier Stevens and it seems to be in the public domain. Thanks, Didier, if you ever read this.
You really need only two of the many features of this tool:
python3 oledump.py -i file.dlv
gives you a listing of the streams (variables) contained in the file, with the index number, stream size, and name.
python3 oledump.py -s<stream #> -d file.dlv
dumps stream #7 as a binary file to standard output, so if you want to dump stream #7, e.g., you have to do:
python3 oledump.py -s7 -d file.dlv > variable7.bin
Now, this is a binary file, with big-endian integers. Your mileage may vary here. In my case, I have found that each record has:
a single byte
a long integer (4 bytes), which is the date and time in POSIX format
a short integer (2 bytes), meaning unknown
another long integer, which is the value measured.
I have written a small piece of Python code that reads the file.
import struct
import binascii
from datetime import datetime, timezone, timedelta
filename = "s7.bin"
# This is the record format
values = '< c L H L' # < big-endian
# c one char (unknown)
# L long integer (date)
# H short integer (unknown)
# L long integer (value)
struct_len = struct.calcsize(values) # record size
struct_unpack = struct.Struct(values).unpack_from # shorthand, really
with open(filename, "rb") as f:
while True:
data = f.read(struct_len) # read each record
if not data:
break
record = struct_unpack(data) # unpack the record
# Now record[1] is a Unix timestamp with the date
# and record[3] is the value
# record[0] and record[2] may be padding, or timezone related (?)
# this is one way to print the time with timezone=UTC+1
print(datetime.fromtimestamp(record[1],tz=timezone(timedelta(hours=1))).strftime('%d-%m-%Y %H:%M:%S0'))
print(record[3])
The whole thing is really fast.
Of course, use at your own risk, I'm not implying that this may be of use to anybody, and so on.