dbdreader 0.6.0.dev1__cp313-cp313-win_amd64.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. _dbdreader.cp313-win_amd64.pyd +0 -0
  2. dbdreader/__init__.py +12 -0
  3. dbdreader/data/01600000.dcd +0 -0
  4. dbdreader/data/01600000.ebd +0 -0
  5. dbdreader/data/01600000.ecd +0 -0
  6. dbdreader/data/01600000.mcg +0 -0
  7. dbdreader/data/01600000.mlg +1904 -0
  8. dbdreader/data/01600001.dbd +0 -0
  9. dbdreader/data/01600001.dcd +0 -0
  10. dbdreader/data/01600001.ebd +0 -0
  11. dbdreader/data/01600001.ecd +0 -0
  12. dbdreader/data/02380107.ecd +0 -0
  13. dbdreader/data/02380108.ecd +0 -0
  14. dbdreader/data/02450133.tcd +0 -0
  15. dbdreader/data/02450137.tcd +0 -0
  16. dbdreader/data/amadeus-2014-203-00-000.SBD +0 -0
  17. dbdreader/data/amadeus-2014-203-00-000.TBD +0 -0
  18. dbdreader/data/amadeus-2014-204-05-000.dbd +0 -0
  19. dbdreader/data/amadeus-2014-204-05-000.ebd +0 -0
  20. dbdreader/data/amadeus-2014-204-05-000.sbd +0 -0
  21. dbdreader/data/amadeus-2014-204-05-000.tbd +0 -0
  22. dbdreader/data/amadeus-2014-204-05-001.sbd +0 -0
  23. dbdreader/data/amadeus-2014-204-05-001.tbd +0 -0
  24. dbdreader/data/amadeus-2014-204-05-002.sbd +0 -0
  25. dbdreader/data/amadeus-2014-204-05-002.tbd +0 -0
  26. dbdreader/data/ammonite-2008-028-01-000.mbd +2091 -1
  27. dbdreader/data/dbd2asc_output.txt +25 -0
  28. dbdreader/data/electa-2023-143-00-050.sbd +0 -0
  29. dbdreader/data/electa-2023-143-00-050.tbd +0 -0
  30. dbdreader/data/empty-2014-204-05-000.dbd +0 -0
  31. dbdreader/data/hal_1002-2024-183-4-4.sbd +0 -0
  32. dbdreader/data/hal_1002-2024-183-4-4.tbd +0 -0
  33. dbdreader/data/hal_1002-2024-183-4-6.tbd +0 -0
  34. dbdreader/data/invalid_encoding-2014-204-05-000.dbd +0 -0
  35. dbdreader/data/sebastian-2014-204-05-000.dbd +0 -0
  36. dbdreader/data/sebastian-2014-204-05-000.ebd +0 -0
  37. dbdreader/data/sebastian-2014-204-05-001.dbd +0 -0
  38. dbdreader/data/sebastian-2014-204-05-001.ebd +0 -0
  39. dbdreader/data/unit_887-2021-321-3-0.sbd +0 -0
  40. dbdreader/data/unit_887-2021-321-3-0.tbd +0 -0
  41. dbdreader/dbdreader.py +2174 -0
  42. dbdreader/decompress.py +247 -0
  43. dbdreader/scripts.py +279 -0
  44. dbdreader-0.6.0.dev1.dist-info/METADATA +272 -0
  45. dbdreader-0.6.0.dev1.dist-info/RECORD +50 -0
  46. dbdreader-0.6.0.dev1.dist-info/WHEEL +5 -0
  47. dbdreader-0.6.0.dev1.dist-info/entry_points.txt +3 -0
  48. dbdreader-0.6.0.dev1.dist-info/licenses/COPYING +340 -0
  49. dbdreader-0.6.0.dev1.dist-info/licenses/LICENSE +674 -0
  50. dbdreader-0.6.0.dev1.dist-info/top_level.txt +2 -0
@@ -0,0 +1,247 @@
1
+ import os
2
+ from io import BytesIO as ioBytesIO
3
+ from re import search as re_match
4
+ import lz4.block
5
+
6
+ DECOMPRESSION_ERROR_LIST = ["NO_ERROR",
7
+ "ERROR_UNEXPECTED_END_OF_FILE",
8
+ "ERROR_FILE_NOT_FOUND",
9
+ "ERROR_FAILED_TO_WRITE_BASE_FILE"]
10
+
11
+ class Decompressor:
12
+ '''Class to decompress glider files
13
+
14
+ Parameters
15
+ ----------
16
+ filename : str
17
+ name of file to decompress
18
+
19
+ This class is designed to be used with a context manager
20
+
21
+ >>> with Decompressor(filename) as d:
22
+ data = d.decompress()
23
+
24
+ Alternatively, a file can be opened a priori
25
+
26
+
27
+ >>> d = Decompressor()
28
+ >>> fd = open(filename ,'rb')
29
+ >>> data = d.decompress(fd)
30
+ '''
31
+ SIZEFIELDSIZE = 2
32
+ ENDIANESS = 'big'
33
+ COMPRESSION_FACTOR=10
34
+ CHUNKSIZE = 1024*32
35
+ def __init__(self, filename=None, fp=None):
36
+ self.filename = filename
37
+ self.fp = fp
38
+
39
+
40
+ def __enter__(self, *p, **kwds):
41
+ # Try to open file is self.filename is given, otherwise check wheter self.fp is given.
42
+ # if not, raise an error.
43
+ if not self.filename is None:
44
+ self.fp = open(self.filename,'rb')
45
+ if self.fp is None:
46
+ raise ValueError('Supply either a filename or file descriptor to the class constructor.')
47
+ return self
48
+
49
+ def __exit__(self, *p, **kwds):
50
+ # close the file and leave.
51
+ self.fp.close()
52
+
53
+ def _decompress_block(self, fp=None):
54
+ fp = fp or self.fp
55
+ sb = fp.read(Decompressor.SIZEFIELDSIZE)
56
+ if sb:
57
+ size = int.from_bytes(sb, Decompressor.ENDIANESS)
58
+ b = lz4.block.decompress(fp.read(size), Decompressor.CHUNKSIZE)
59
+ else:
60
+ b = None
61
+ return b
62
+
63
+ def decompressed_blocks(self, n=None, fp=None):
64
+ ''' Generator that returns decompressed data blocks
65
+
66
+ Parameters
67
+ ----------
68
+ n : int or None
69
+ limits the number of blocks read and returned. If None (default) all blocks are returned
70
+ fp : file descriptor or None
71
+ file descriptor to use. If None (default), the file descriptor assigned by the constructor is used.
72
+
73
+ Yields
74
+ ------
75
+ bytes:
76
+ decompressed data block
77
+ '''
78
+ counter = 0
79
+ if n is None:
80
+ counter_increment = 0
81
+ n = 1
82
+ else:
83
+ counter_increment = 1
84
+ while counter < n:
85
+ block = self._decompress_block(fp)
86
+ if block is None:
87
+ break
88
+ yield block
89
+ counter+=counter_increment
90
+
91
+ def decompress(self, fp=None):
92
+ ''' Decompresses a an entire file (in memory)
93
+
94
+ Parameters
95
+ ----------
96
+ fp : file descriptor or None
97
+ file descriptor to use. If None (default), the file descriptor assigned by the constructor is used.
98
+
99
+ Returns
100
+ -------
101
+ bytes:
102
+ decompressed file data as bytes
103
+ '''
104
+ fp = fp or self.fp
105
+ if fp is None:
106
+ raise ValueError('Supply a file handler or use this class within a context manager')
107
+ data =b''
108
+ for block in self.decompressed_blocks(fp=fp):
109
+ data += block
110
+ return data
111
+
112
+
113
+ class FileDecompressor:
114
+ '''Class that provides an easy way to automatically decompress a compressed glider
115
+ data file and write it into a normal binary data file.
116
+
117
+ The factual decompressing is done by decompress method.
118
+
119
+ Example
120
+ -------
121
+
122
+ >>> FileDecompressor.decompress("01600000.dcd")
123
+
124
+ which would result in the writing of a decompressed file 01600000.dbd.
125
+
126
+ '''
127
+ def _generate_filename_for_output(self, filename):
128
+ base, ext = os.path.splitext(filename)
129
+ if len(ext)!=4:
130
+ raise ValueError('Unhandled file extension.')
131
+ if ext.endswith('cg'):
132
+ s = 'lg'
133
+ elif ext.endswith('cd'):
134
+ s = 'bd'
135
+ elif ext.endswith('cc'):
136
+ s = 'ac'
137
+ else:
138
+ raise ValueError('Unhandled file extension.')
139
+ return "".join((base, ext[:-2], s))
140
+
141
+ def decompress(self, filename):
142
+ ''' Decompresses a file
143
+
144
+ Parameters
145
+ ----------
146
+ filename : str
147
+ (compressed) filename
148
+
149
+ Returns
150
+ -------
151
+ str:
152
+ uncompressed filename
153
+
154
+ '''
155
+ output_filename = self._generate_filename_for_output(filename)
156
+ with Decompressor(filename) as d, open(output_filename, 'wb') as fp_out:
157
+ for block in d.decompressed_blocks():
158
+ fp_out.write(block)
159
+ return output_filename
160
+
161
+ def decompress_file(filename):
162
+ '''Decompreses a glider data file and writes the normal binary file.'''
163
+ return FileDecompressor().decompress(filename)
164
+
165
+ def is_compressed(filename):
166
+ [basename,ext]=os.path.splitext(filename)
167
+ # all compressed [demnst]bd files end in [demnst]cd
168
+ return bool(re_match("[demnst]c[dg]$", ext))
169
+
170
+
171
+ class BytesIORW:
172
+ ''' Helper class implementing a BytesIO buffer that can be written to and read from.
173
+
174
+ Note that the methods write() and readline() are implemented only.
175
+ '''
176
+ def __init__(self, source):
177
+ self.bytesIO = ioBytesIO()
178
+ self.pointer_start = 0
179
+ self.pointer_end = 0
180
+ self.source = source
181
+
182
+ def write(self, b):
183
+ self.pointer_start = self.bytesIO.tell()
184
+ self.bytesIO.write(b)
185
+ self.pointer_end = self.bytesIO.tell()
186
+ self.bytesIO.seek(self.pointer_start)
187
+ self.is_exhausted = False
188
+
189
+ def readline(self):
190
+ line = self.bytesIO.readline()
191
+ pointer = self.bytesIO.tell()
192
+ if pointer == self.pointer_end:
193
+ try:
194
+ self.write(next(self.source))
195
+ except StopIteration:
196
+ pass
197
+ else:
198
+ line+=self.bytesIO.readline()
199
+ return line
200
+
201
+
202
+ class CompressedFile:
203
+ ''' Class to access a compressed file, providing a method
204
+
205
+ readline() that returns a decompressed line of data. The compressed
206
+ file is read block by block, as long as needed.
207
+
208
+ The main reason for the class is to be able to read the header of a compressed
209
+ glider data file.
210
+ '''
211
+
212
+
213
+ def __init__(self, filename):
214
+ self.filename = filename
215
+ self.decompressor = Decompressor()
216
+ self.bytesIO = None
217
+
218
+ def __enter__(self, *p, **kwds):
219
+ self.fp = open(self.filename, 'rb')
220
+ source = self.decompressor.decompressed_blocks(fp=self.fp)
221
+ self.bytesIO = BytesIORW(source)
222
+ return self
223
+
224
+
225
+ def __exit__(self, *p, **kwds):
226
+ self.fp.close()
227
+
228
+ def readline(self):
229
+ line = self.bytesIO.readline()
230
+ return line
231
+
232
+ def readlines(self):
233
+ while True:
234
+ line = self.readline()
235
+ if not line:
236
+ break
237
+ yield line
238
+
239
+ def seek(self, offset):
240
+ return self.bytesIO.bytesIO.seek(offset)
241
+
242
+ def tell(self):
243
+ return self.bytesIO.bytesIO.tell()
244
+
245
+
246
+ def close(self):
247
+ self.fp.close()
dbdreader/scripts.py ADDED
@@ -0,0 +1,279 @@
1
+ # Copyright 2006,2007,2014, 2023 Lucas Merckelbach
2
+ #
3
+ # This file is part of dbdreader.
4
+ #
5
+ # dbdreader is free software: you can redistribute it and/or modify
6
+ # it under the terms of the GNU General Public License as published by
7
+ # the Free Software Foundation, either version 3 of the License, or
8
+ # (at your option) any later version.
9
+ #
10
+ # dbdreader is distributed in the hope that it will be useful,
11
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ # GNU General Public License for more details.
14
+ 6#
15
+ # You should have received a copy of the GNU General Public License
16
+ # along with dbdreader. If not, see <http://www.gnu.org/licenses/>.
17
+ #
18
+
19
+ from collections import OrderedDict
20
+ import sys
21
+ import os
22
+ import argparse
23
+ import re
24
+
25
+
26
+
27
+ import dbdreader.decompress
28
+
29
+
30
+ def _makeSortable(filename):
31
+ [basename,ext]=filename.split('.')
32
+ [g,y,d,m,s]=basename.split('-')
33
+ s="%03d"%(int(s))
34
+ m="%02d"%(int(m))
35
+ d="%03d"%(int(d))
36
+ basename="-".join([g,y,d,m,s])
37
+ filename=".".join([basename,ext])
38
+ return(filename)
39
+
40
+
41
+
42
+
43
+ def _get_short_and_long_filenames(lines, filename):
44
+ ID=lines[0]
45
+ ignoreIt=False
46
+ shortfilename=''
47
+ longfilename=''
48
+ if ID=='dbd_label: DBD(dinkum_binary_data)file':
49
+ shortfilename=lines[4]
50
+ longfilename=lines[5]
51
+ shortfilename=re.sub(r"^.* ","",shortfilename)
52
+ longfilename=re.sub(r"^.* ","",longfilename)
53
+ extension=re.sub(r"^.*\.","",filename)
54
+ shortfilename+="."+extension
55
+ longfilename+="."+extension
56
+ elif "the8x3_filename" in ID: # mlg file apparently...
57
+ longfilename=lines[1]
58
+ if filename.lower().endswith('mlg'):
59
+ extension='.mlg'
60
+ else:
61
+ extension='.nlg'
62
+ shortfilename=re.sub(r"^the8x3_filename: +","",ID+extension)
63
+
64
+ longfilename=re.sub(r"^full_filename: +","",longfilename)+extension
65
+ else:
66
+ sys.stderr.write("Ignoring %s\n"%(filename))
67
+ ignoreIt=True
68
+ return ignoreIt, shortfilename, longfilename
69
+
70
+ def dbdrename():
71
+ ''' Standalone script to rename dbd files
72
+
73
+ Use dbdrename -h for help.
74
+ '''
75
+ parser = argparse.ArgumentParser(
76
+ prog='dbdrename',
77
+ description='''Program to rename dbd files and friends from numeric format to long format, or vice versa,
78
+ or convert the long format into a sortable name or the original Webb Research long format,
79
+ or decompress LZ4 compressed files.''',
80
+ epilog='')
81
+
82
+ parser.add_argument('filenames', nargs="*", help="Filename(s) to process")
83
+ parser.add_argument('-s', action='store_true', default=True, help='Ensures long format filenames are sortable')
84
+ parser.add_argument('-n', action='store_true', help='Keeps the original long format filenames (which do not sort correctly).')
85
+ parser.add_argument('-c', '--convertToSortable', action='store_true', help='Converts files from original long format to a sortable long format')
86
+ parser.add_argument('-C', '--convertToOriginal', action='store_true', help='Converts files from a sortable long format to the original long format')
87
+ parser.add_argument('-x', '--decompress', action='store_true', help='Decompresses LZ4 comrpessed files.')
88
+ parser.add_argument('-X', '--decompressAndRemoveCompressed', action='store_true', help='Decompresses LZ4 comrpessed files, and removes the compressed file')
89
+ parser.add_argument('-d', '--doNotChangeNameFormat', action='store_true', help='Does not change the filename format. (Only useful in combination with -x or -X)')
90
+
91
+
92
+
93
+ args = parser.parse_args()
94
+
95
+ # override the default value of -s if -d is specified
96
+ if args.doNotChangeNameFormat:
97
+ args.s=False
98
+
99
+ for i in args.filenames:
100
+ if not os.path.exists(i):
101
+ print(f"{i} does not exist. Ignoring.")
102
+ continue
103
+ if dbdreader.decompress.is_compressed(i):
104
+ with dbdreader.decompress.CompressedFile(i) as fd:
105
+ lines = [fd.readline().decode('ascii').strip() for j in range(7)]
106
+ else:
107
+ with open(i,'br') as fd:
108
+ lines = [fd.readline().decode('ascii').strip() for j in range(7)]
109
+ ignoreIt, shortfilename, longfilename = _get_short_and_long_filenames(lines, i)
110
+
111
+ basename = os.path.basename(i)
112
+ prefix = i.replace(basename, "")
113
+
114
+ if not ignoreIt:
115
+ command=None
116
+ if args.convertToSortable or args.convertToOriginal: # input filename must be the longfilename
117
+ # check if we have a longfilename
118
+ if i not in [longfilename,_makeSortable(longfilename)]:
119
+ print("Chose to ignore processing ",i)
120
+ else:
121
+ if i==_makeSortable(longfilename) and args.convertToOriginal:
122
+ # switch to old format
123
+ command="mv "+i+" "+longfilename
124
+ new_filename = longfilename
125
+ elif i==longfilename and args.convertToSortable:
126
+ command="mv "+longfilename+" "+_makeSortable(longfilename)
127
+ new_filename = _makeSortable(longfilename)
128
+ else:
129
+ print("ignoring "+i)
130
+ else: # changing from long to short names or vice versa
131
+ if not args.n and not args.s: # -d has been requested
132
+ new_filename = i
133
+ if args.decompress or args.decompressAndRemoveCompressed:
134
+ command = ""
135
+ else: # create the new_filename by translating
136
+ if args.s and not args.n:
137
+ longfilename=_makeSortable(longfilename)
138
+ if basename==shortfilename:
139
+ new_filename = os.path.join(prefix, longfilename)
140
+ old_filename = os.path.join(prefix, shortfilename)
141
+ command = f"mv {old_filename} {new_filename}"
142
+ else:
143
+ new_filename = os.path.join(prefix, shortfilename)
144
+ old_filename = os.path.join(prefix, longfilename)
145
+ command = f"mv {old_filename} {new_filename}"
146
+
147
+ if command!=None:
148
+ target = new_filename
149
+ if command:
150
+ R=os.system(command)
151
+ else:
152
+ R=0
153
+ msg = f"{i} ->"
154
+ if R!=0:
155
+ raise ValueError("Could not execute %s"%(command))
156
+ if args.decompress or args.decompressAndRemoveCompressed:
157
+ try:
158
+ target = dbdreader.decompress.decompress_file(new_filename)
159
+ except Exception as e:
160
+ msg = e
161
+ else:
162
+ if args.decompressAndRemoveCompressed:
163
+ os.unlink(new_filename)
164
+ msg = f"{msg} {target}"
165
+ else:
166
+ msg = f"{msg} {target}/{new_filename}"
167
+ else:
168
+ msg = f"{msg} {target}"
169
+ print(msg)
170
+
171
+
172
+ ''' =====================================================================================================
173
+
174
+ cac_gen
175
+
176
+ ===================================================================================================== '''
177
+
178
+
179
+ def _parse_line(line):
180
+ fields=line.rstrip().split()
181
+ s=dict(flag=fields[1],
182
+ global_index=int(fields[2]),
183
+ index=int(fields[3]),
184
+ bytesize=int(fields[4]),
185
+ name=fields[5],
186
+ unit=fields[6])
187
+ return s['name'],s
188
+
189
+ def _read_cac_file(fn):
190
+ sensors=OrderedDict()
191
+ fd=open(fn,'r')
192
+ while True:
193
+ line=fd.readline()
194
+ if not line:
195
+ break
196
+ n,s=_parse_line(line)
197
+ sensors[n]=s
198
+ fd.close()
199
+ return sensors
200
+
201
+ def _read_mbdlist(fn):
202
+ params=[]
203
+ fd=open(fn,'r')
204
+ while True:
205
+ line=fd.readline()
206
+ if not line:
207
+ break
208
+ line=line.rstrip().lstrip()
209
+ if not line:
210
+ continue # empty line
211
+ if line.startswith("#"):
212
+ continue
213
+ s=line.lower().split()
214
+ params.append(s[0])
215
+ fd.close()
216
+ return params
217
+
218
+ def _generate_cac(s,params):
219
+ # sort params:
220
+ params_in_order=[]
221
+ for k in list(s.keys()):
222
+ if k in params:
223
+ params_in_order.append(k)
224
+ # set all indices to -1
225
+ for v in s.values():
226
+ v['index']=-1
227
+ v['flag']='F'
228
+ for i,p in enumerate(params_in_order):
229
+ s[p]['index']=i
230
+ s[p]['flag']='T'
231
+ return s
232
+
233
+ def _write_cac(s,fd):
234
+ for k,v in s.items():
235
+ fd.write("s: %s%5d%5d%2d %s %s\n"%(v['flag'],
236
+ v['global_index'],
237
+ v['index'],
238
+ v['bytesize'],
239
+ v['name'],
240
+ v['unit']))
241
+
242
+
243
+ def _generate_cac_file(mbdlist_filename,template,output):
244
+ s=_read_cac_file(template)
245
+ params=_read_mbdlist(mbdlist_filename)
246
+ s=_generate_cac(s,params)
247
+ if output:
248
+ fd=open(output,'w')
249
+ _write_cac(s,fd)
250
+ fd.close()
251
+ else:
252
+ _write_cac(s,sys.stdout)
253
+
254
+
255
+ def cac_gen():
256
+ ''' A script to generate cac files
257
+
258
+ run cac_gen -h for more help.
259
+ '''
260
+ parser = argparse.ArgumentParser(
261
+ prog='cac_gen',
262
+ description='''A program to generate a valid cache file from a .dat file and a valid cache file for this
263
+ glider.
264
+
265
+ For example, if you have a cache file for an sbd file, and the sbdlist.dat got modified, then this programs
266
+ let's you build a new cache file using the old cache file and new sdblist.dat file as input.
267
+ ''',
268
+ epilog='')
269
+
270
+ parser.add_argument('sdblist', help="sdblist.dat file, specifying which parameters are present in the binary files")
271
+ parser.add_argument('cachefile', help="A valid and exisiting cache file for this glider.")
272
+ parser.add_argument('outputfile', nargs="?", default=None, help="Output filename (writes to stdout if not speficied.)")
273
+
274
+ args = parser.parse_args()
275
+ _generate_cac_file(args.sdblist, args.cachefile, args.outputfile)
276
+
277
+
278
+
279
+