diffbmuΒΆ

A script to compute diff between two bmu files.

#!/usr/bin/env python
''' A script to compute diff between two bmu files.
'''

import os
import sys
import logging
logging.basicConfig()
logger = logging.getLogger("diffbmu")

from optparse import OptionParser
import datetime

import Sadppac

from irfpy.util import utc

def diff(lfile, rfile, **kwds):
    '''
    '''
    loader = kwds.get('loader', 'CENA')
    logger.debug("Loader = %s" % loader)

    fullmd5 = kwds.get('fullmd5', False)
    if fullmd5:
        col = -1
        coldiff = 0
    else:
        col = 7
        coldiff = 25
        
    allshow = kwds.get('allshow', False)

    logger.debug('lfile = %s' % lfile)
    if not os.path.exists(lfile):
        logger.error('!!!! No file exists %s' % lfile)
        raise RuntimeError('!!!! No file exists %s' % lfile)

    logger.debug('rfile = %s' % rfile)
    if not os.path.exists(rfile):
        logger.error('!!!! No file exists %s' % rfile)
        raise RuntimeError('!!!! No file exists %s' % rfile)

    if loader == "CENA":
        reader1 = Sadppac.CenaBmuReader()
        reader2 = Sadppac.CenaBmuReader()
    elif loader == "SWIM":
        reader1 = Sadppac.SwimBmuReader()
        reader2 = Sadppac.SwimBmuReader()
    else:
        raise RuntimeError("Loader %s not supported." % (loader))

    # Read data
    reader1.readFile(lfile)
    reader2.readFile(rfile)

    packets1 = reader1.getArray()
    packets2 = reader2.getArray()

    # BMU size check

    npac1 = packets1.size()
    npac2 = packets2.size()

    logger.debug('Number of packet: L=%d  R=%d' % (npac1, npac2))

    print('### Diff  %s  %s' % (lfile, rfile))

    if npac1 != npac2:
        print('!!!! Size differs:')
        print('!!!!     N=%d  %s' % (npac1, lfile))
        print('!!!!     N=%d  %s' % (npac2, rfile))
        print('!!!!!!!!!!!!!!!!!!!!!!!!')
    else:
        if allshow:
            print('oooo Size identical:')
            print('oooo     N=%d  %s' % (npac1, lfile))
            print('oooo     N=%d  %s' % (npac2, rfile))
            print('oooooooooooooooooooooooo')
    

    # You cannot assume ascending or descending array order.

    pacArr1 = [packets1.getPacket(i) for i in range(npac1)]
    pacArr2 = [packets2.getPacket(i) for i in range(npac2)]

    tlist1 = [utc.convert(pac.getUtc(), datetime.datetime) for pac in pacArr1]
    tlist2 = [utc.convert(pac.getUtc(), datetime.datetime) for pac in pacArr2]

    mlist1 = [pac.hash() + ' ' for pac in pacArr1]
    mlist2 = [pac.hash() + ' ' for pac in pacArr2]   # Space is for padding.

    # Order should be organized by the left file's index.
    # To be compared is time and md5.

    idx_match={}   # A pair of indexes in mlist1 and mlist2.  (1 to X)

    for idx, (pac, tim, md5) in enumerate(list(zip(pacArr1, tlist1, mlist1))):

        # Check if same md5 is in the right file

        # Four cases:   1 to 0 / 1 to 1  / 1 to mul / 0 to 1
        #  (mul to 1 is not considered.)
        idx2 = -1
        while True:
            try:
                # Update the list
                idx2 = mlist2.index(md5, idx2+1)
                idx2now = idx_match.get(idx, [])
                idx2now.append(idx2)
                idx_match[idx] = idx2now
            except ValueError:
                break

    # Here, idx2 search should be employed for "0 to 1" case.  Not yet implemented.
    idx2set = set()
    for idx in list(idx_match.keys()):
        idx2list = idx_match[idx]
        for idx2 in idx2list:
            idx2set.add(idx2)

    # For printing.  Prepare string array first.
    output_str = []

    # Now printing
    for idx in range(npac1):
        idx2list = idx_match.get(idx, [])
        if len(idx2list) == 0:   # 1 to 0
            output_str.append(['D   ' +
                             '%04d  %s  %s | (No value)' % (idx, tlist1[idx].strftime('%FT%T'), mlist1[idx][:col])])
        elif len(idx2list) == 1:  # 1 to 1
            idx2 = idx2list[0]
            output_str.append(['    ' + '%04d  %s  %s | %04d  %s  %s' % (idx, tlist1[idx].strftime('%FT%T'), mlist1[idx][:col], idx2, tlist2[idx2].strftime('%FT%T'), mlist2[idx2][:col])])
        else:
            idx2 = idx2list[0]
            substr = []
            substr.append('M   ' + '%04d  %s  %s | %04d  %s  %s' % (idx, tlist1[idx].strftime('%FT%T'), mlist1[idx][:col], idx2, tlist2[idx2].strftime('%FT%T'), mlist2[idx2][:col]))
            for i in range(1, len(idx2list)):
                idx2 = idx2list[i]
                substr.append('M   ' + '%04d' % (idx) + (' ' * (56 - coldiff))+'| %04d  %s  %s ' %(idx2, tlist2[idx2].strftime('%FT%T'), mlist2[idx2][:col]))
            output_str.append(substr)

    # Search the place of the 0 to 1 case.
    for idx2 in range(npac2):
        if not idx2 in idx2set:  # idx2 is 0 to 1 corresp.  Search the place of pushing
            substr_nu=['A   ' + '****  (No value)' + (' ' * (44 - coldiff)) + '| %04d  %s  %s ' %(idx2, tlist2[idx2].strftime('%FT%T'), mlist2[idx2][:col])]
            here = False
            for istr in range(len(output_str)):
                substr = output_str[istr]
                for str in substr:
                    idx_nu = str.find('|') + 2    # Here assuming the format of the ouput.  First | followed by space is delimiter, and 4 digit pacnr follows.
                    try:
                        idx2_nu = int(str[idx_nu:idx_nu+4])
                        if idx2_nu == idx2 - 1:
                            here=True
                    except ValueError:
                        pass
                if here:
                    output_str.insert(istr+1, substr_nu)
                    break
            if not here:
                output_str.append(substr_nu)

    for substr in output_str:
        for str in substr:
            if str.startswith(' '):   # This is 'consistent' packets.
                if allshow:
                    print(str)
            else:
                print(str)
            

def main(*argv, **kwds):
    usage = "Usage:  %prog  [options]  bmufile1.bmu  bmufile2.bmu"

    parser = OptionParser(usage)
    parser.add_option("-v", action="store_true", dest="verbose",
                    default=False, help="Verbose mode")
    parser.add_option("-s", action="store_true", dest="useswim",
                    default=False, help="Use SWIM BMU loader (default CENA)")
    parser.add_option("-f", action="store_true", dest="fullmd5",
                    default=False, help="Show full md5 value.")
    parser.add_option("-a", action="store_true", dest="allshow",
                    default=False, help="Show agreed packet too.")

    (options, args) = parser.parse_args(*argv[1:])

    if options.verbose:
        logger.setLevel(logging.DEBUG)
        logger.debug("Set verbose mode")

    loader="CENA"
    if options.useswim:
        loader="SWIM"
    logger.debug("Loader = %s" % loader)

    if len(args) != 2:
        parser.error("!!!! Two files should be specified")

    diff(args[0], args[1], loader=loader, fullmd5=options.fullmd5, allshow=options.allshow)


if __name__ == '__main__':
    main(sys.argv)