<gh_stars>1-10
#******************************************************************************
#
#   Filename:       FuelGaugeProgrammer
#
#   Description:    Takes a text file output from TI's Battery Management
#                   Studio software and converts it into an .h file that can be
#                   combined with Arduino or Energia firmware to program a TI
#                   fuel gauge.
#
#   Note:           The write commands which actually carry data to be written
#                   to flash have the undocumented feature that they cannnot
#                   contain more than 30 bytes of data.  Further, the first byte
#                   of data is an index specifying where within the data block
#                   the data is to be written.  So for example, this command:
#
#                   W: 16 00 AB CD EF AB EA FF 33 A0 FA 33 12 FB 33 8C FE
#                         33 D1 FE 33 FF FF 3F 40 05 00 FF FF 3F FF FF 3F
#                         FF FF 3F FF FF 3F FF FF 3F FF FF 3F FF FF 3F FF
#                         FF 3F FF FF 3F 01 02 3F 58 CB 33 FB AB 08 01 CD
#                         EF AB CD EF FF AA 0E FB A7 0E FF A6 0E FF A1 0E
#                         FF A0 0E FE A3 0E 62 A2 0E D2 FF 3A FF A1 0E 63
#                         A0 0E FF A3 0E
#
#                   The byte "00" is the index within the flash block where the
#                   rest of the data will be written.  This command must be
#                   split like this:
#
#                   W: 16 00 AB CD EF AB EA FF 33 A0 FA 33 12 FB 33 8C FE
#                      33 D1 FE 33 FF FF 3F 40 05 00 FF FF 3F FF
#                   W: 16 1D FF 3F FF FF 3F FF FF 3F FF FF 3F FF FF 3F FF
#                      FF 3F FF FF 3F FF FF 3F 01 02 3F 58 CB 33
#                   W: 16 3A FB AB 08 01 CD EF AB CD EF FF AA 0E FB A7 0E
#                      FF A6 0E FF A1 0E FF A0 0E FE A3 0E 62 A2
#                   W: 16 57 0E D2 FF 3A FF A1 0E 63 A0 0E FF A3 0E
#
#                   Note that these commands each contain no more than 30 bytes
#                   to be sent over I2C, including an index byte that was
#                   calculated and added: "00", "1D", "3A", "57".
#
#                   Note also that not all write commands carry data to be
#                   written to flash, so this rule does not apply to them.  It
#                   seems safe to apply this rule to any write command longer
#                   than 30 bytes, and omit it for shorter commands.
#
#******************************************************************************

#******************************************************************************
# Copyright (c) 2017 Dwyer Instruments, Inc.
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
# 
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
# 
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
#******************************************************************************

def Convert(inputFile, outputFile = 'data.h'):
    """Converts a text file to a C header file."""

    # Create a constant for max length of a write command.
    WRITE_MAX = 30

    # Open the file for reading.
    textFile = open(inputFile)
    print "Opened the firmware file:      ", textFile.name

    # Create the new output file; erase a preexisting output file.
    hFile = open(outputFile, "w")
    print "Created/overwrote output file: ", hFile.name

    # Add stuff to the beginning of the h file.
    lines = ("\n/* Autogenerated by FuelGaugeProgrammer.py */\n\n"
             "#ifndef DATA_H\n"
             "#define DATA_H\n\n"
             "// This is data to be written to the device.\n"
             "#ifndef DATA_ARRAY\n"
             "#define DATA_ARRAY\n"
             "const uint8_t data[] =\n"
             "{//          cmd  addr  len   data\n")
    hFile.write(lines)

    print "Encoding the following data:"
    
    # Step through each line in the file.
    for line in textFile:
        # Ignore lines which start with ';'.
        if line[0] == ';':
            pass

        # For all other lines...
        else:
            # Remove the newline character.
            line = line.strip()
            
            # Split the string into a list of data.
            lineItems = line.split(' ')

            # Find the command substring (should be either 'W:', 'C:', 'X:').
            command = lineItems[0]
            
            # Process the delay command "X".
            if command == 'X:':
                # Print the command.
                hFile.write("\t(uint8_t)\'X\',")
                print "X:",

                # Find the number of milliseconds to delay.
                delay = lineItems[1]

                # Split the delay into two bytes.
                highByte = (int(delay) >> 8) & 0xFF
                lowByte = int(delay) & 0xFF

                # Print both bytes as hexidecimal numbers.
                hFile.write(' 0x{:02X}, 0x{:02X},'.format(highByte, lowByte))
                hFile.write("\t// {:s} ms delay\n".format(delay))
                print '0x{:02X}, 0x{:02X},'.format(highByte, lowByte),
                print '(' + delay, 'ms)'

            # Process the compare command "C".
            elif command == 'C:':
                # Print the command.
                hFile.write("\t(uint8_t)\'C\',")
                print "C:",
                
                # Find the address.
                address = lineItems[1]

                # Print the address.
                hFile.write(" 0x" + address + ',')
                print "0x" + address + ',',

                # Form list of the remaining items.
                dataItems = lineItems[2:]

                # Find the number of data items.
                length = len(dataItems)

                # Print the number of data items as a hexidecimal number.
                hFile.write(" 0x{:02X},".format(length))
                print "%3d, " %length,

                # Print the data items.
                for eachItem in dataItems:
                    hFile.write(" 0x" + eachItem + ',')
                    print "0x" + eachItem + ',',

                # Print a newline.
                hFile.write('\n')
                print

            # Process the write command "W".
            elif command == 'W:':
                # Find the address.
                address = lineItems[1]

                # Form list of the remaining items.
                dataItems = lineItems[2:]

                # Find the number of data items.
                length = len(dataItems)

                # Remember our current position in the dataItems list.
                index = 0

                # Process all data items.
                while length:
                    # Print the command.
                    hFile.write("\t(uint8_t)\'W\',")
                    print "W:",

                    # Print the address.
                    hFile.write(" 0x" + address + ',')
                    print "0x" + address + ',',
                    
                    # Split into commands of no more than 30 bytes.
                    if length > WRITE_MAX:
                        # Print 30 (the number of data items = WRITE_MAX).
                        hFile.write(" 0x{:02X},".format(WRITE_MAX))
                        print "%3d, " %WRITE_MAX,

                        # Print the index, but not for the first packet,
                        # because it's already there (see note at top of file).
                        # If we aren't on the first chunk of 30...
                        if index == 0:
                            # Print the flash index, which is the first data item.
                            hFile.write(" 0x" + dataItems[0] + ',')
                            print "0x" + dataItems[0] + ',',

                            # Delete the flash index from the dataItems list.
                            dataItems = dataItems[1:]
                        else:
                            # Print the flash index, which we calculated.
                            hFile.write(" 0x{:02X},".format(index))
                            print '0x{:02X},'.format(index),

                        # Print 29 data items (29 = WRITE_MAX - 1).
                        for eachItem in dataItems[index:index + WRITE_MAX - 1]:
                            hFile.write(" 0x" + eachItem + ',')
                            print "0x" + eachItem + ',',

                        # Subtract 29 from length (29 = WRITE_MAX - 1).
                        length -= WRITE_MAX - 1

                        # Add 29 to index (29 = WRITE_MAX - 1).
                        index += WRITE_MAX - 1
                        
                    else:
                        # Print the number of data items as a hexidecimal number.
                        hFile.write(" 0x{:02X},".format(length))
                        print "%3d, " %length,

                        # Print the flash index, but not for the first packet,
                        # because it's already there (see note at top of file).
                        # If we aren't on the first chunk of 30...
                        if index != 0:
                            # Print the flash index.
                            hFile.write(" 0x{:02X},".format(index))
                            print '0x{:02X},'.format(index),

                        # Print the data items.
                        for eachItem in dataItems[index:]:
                            hFile.write(" 0x" + eachItem + ',')
                            print "0x" + eachItem + ',',

                        # Update length.
                        length = 0

                    # Print a newline.
                    hFile.write('\n')
                    print

            # Deal with erroneous command characters.
            else:
                print "ERROR:  Unexpected command:  " + command
                    

    # Add stuff to the end of the h file.
    lines = ("};\n"
             "#else\n"
             "extern uint8_t data[];\n"
             "#endif\t// DATA_ARRAY\n\n"
             "#endif\t// DATA_H")
    hFile.write(lines)

    # Close the input file.
    textFile.close()
    print "Closed firmware file:          ", textFile.name

    # Close the output file.
    hFile.close()
    print "Closed output file:            ", hFile.name

    print "Done!"

# allow this module to work as a script
if __name__ == '__main__':
    import sys
    #Convert(sys.argv[1])
    Convert("firmware.fs")
