barnhill / barcodelib

C# Barcode Image Generation Library
Apache License 2.0
744 stars 239 forks source link

MSI check digit(s) calculation #171

Closed rob313663 closed 1 year ago

rob313663 commented 1 year ago

Hi!

I implemented MSI today. There is no good documentation available. Especially for the mod 11 algo, none say what should happen when the result is 10 from the mod 11 operation, I assumed it goes to 0 which seems to work.

I have been testing MSI with a Zebra TC52 Android device, I can’t say I trust the implementation of MSI in it to be correct though.

I will redo these tests using a Honeywell CT40 later, when I have been to the office and stolen one.

The TC52 doesn’t seem to have an option of using one check digit in modulo 11 since when set to One Check Digit and Check Digit Scheme Mod-11-10 it still does mod 10 check digit checking. So I have tested:

In the first two tests both my code and BarcodeLib generates the same check digits and successfully generates codes that can be read with the Zebra TC52. But in the last test, some codes generated by BarcodeLib can’t be read.

Test 3:

Setting in TC52: Two Check Digits Check Digit Scheme: Mod-11-10

My code:

MSICheckCharacterAlgorithm.Modulo1110

123456789012057
123456789012131
123456789012214
123456789012305
123456789012487
123456789012560
123456789012644
123456789012727
123456789012800
123456789012990

All can be read with TC52.

BarcodeLib:

type = Type.MsiMod11Mod10

123456789012057
123456789012131
123456789012214
1234567890123?? Not readable
123456789012487
1234567890125?? Not readable
123456789012644
123456789012727
1234567890128?? Not readable
1234567890129?? Not readable

I can’t spot the difference in the mod 11 calculation, but this is my implementation:

MSI.codeWordToChar[codeWordValue] just translates 0-9 to '0'-'9'.

codewordPatterns[c].CodeWordValue just translates '0'-'9' to 0-9.
private string CalculateChecksumChar(string code)
{
    switch (CheckCharacter)
    {
        case MSICheckCharacterAlgorithm.None:
            return "";
        case MSICheckCharacterAlgorithm.Modulo10:
            return ChecksumMod10(code);
        case MSICheckCharacterAlgorithm.Modulo11:
            return ChecksumMod11(code);
        case MSICheckCharacterAlgorithm.Modulo1010:
            {
                var firstCheck10 = ChecksumMod10(code);
                var secondCheck10 = ChecksumMod10(code + firstCheck10);
                return firstCheck10 + secondCheck10;
            }
        case MSICheckCharacterAlgorithm.Modulo1110:
            {
                var firstCheck11 = ChecksumMod11(code);
                var secondCheck10 = ChecksumMod10(code + firstCheck11);
                return firstCheck11 + secondCheck10;
            }
        default:
            return "";
    }
}

private string ChecksumMod11(string code)
{
    var sum = 0;
    int weight = 2;

    for (int i = code.Length - 1; i >= 0; i--)
    {
        var c = code[i];
        var codeValue = codewordPatterns[c].CodeWordValue;

        sum += codeValue * weight;

        weight++;

        // the IBM algorithm which uses (2,3,4,5,6,7)
        if (weight > 7)
        {
            weight = 2;
        }
    }

    var codeWordValue = (11 - (sum % 11)) % 11;

    if (codeWordValue == 10) codeWordValue = 0;

    return MSI.codeWordToChar[codeWordValue].ToString();
}

private string ChecksumMod10(string code)
{
    var sum = 0;
    int weight = 2;

    for (int i = code.Length - 1; i >= 0; i--)
    {
        var c = code[i];
        var codeValue = codewordPatterns[c].CodeWordValue;

        codeValue *= weight;

        int ones = codeValue % 10;
        int tens = codeValue / 10;

        sum += ones + tens;

        // Toggle between 1 and 2
        weight ^= 3;
    }

    var codeWordValue = (10 - (sum % 10)) % 10;

    return MSI.codeWordToChar[codeWordValue].ToString();
}
barnhill commented 1 year ago

Fixed with https://github.com/barnhill/barcodelib/commit/7b6fda9ee99b5485c205abcccadc10cbcd8b005d

Verified with online barcode reader for all Mod types