pythonbashbit-manipulationsnmpnet-snmp

What is a Readable/Modern Way to Parse a Bit-Based Error Code?


I'm tasked with reading error codes from printers via snmp. Luckily, I have a working bash script to guide me through this arcane task. I'm writing some python to do some different work from the existing script. The existing code seems to work, but it's crazy ugly, and I'm hoping for a more python-y way to parse bits.

First, to explain my reading of the current code. The error code returned by an snmpwalk query on hrPrinterDetectedErrorState is encoded as an octet string, often with quotes around it. So the quotes are stripped along with whitespace and newlines. The error code can be up to four bytes, but a null byte is generally sent for the second pair if zero, so a pair of zeros is added in that case. Then the error code is converted to hex.

The existing bash script:

parseErrorState()
{
  setUpErrorCodes
  # pull the error state w/o quotes
  errorState=`snmpwalk -Oqvx -c public -v $snmpV $printerIP hrPrinterDetectedErrorState | grep -v "End of MIB" | tr -d '"'`
  # remove spaces
  errorCode=$(echo $errorState | tr -d [:space:])
  errorString=""

  # if we don't have two hex bytes, append a byte of zeros
  if [[ ${#errorCode} == 2 ]]
  then
    errorCode=$errorCode"00"
  fi

  # do hex conversion
 let errorCode=0x$errorCode

 if (( $errorCode & $overduePreventMaint ))
 then
   errorString=$errorString"Overdue Preventative Maintenance; "
 fi
 if (( $errorCode & $inputTrayEmpty ))
 then
   errorString=$errorString"Input Tray Empty; "
 fi
 if (( $errorCode & $outputFull ))
 then
   errorString=$errorString"Output Full; "
 fi
 if (( $errorCode & $outputNearFull ))
 then
 ... and about 12 more if-thens...

That series of if-thens bit-wise compares the errorCode with each of these and adds a relevant string to the output.

setUpErrorCodes()
  {
  lowPaper=32768
  noPaper=16384
  lowToner=8192
  noToner=4096
  doorOpen=2048
  jammed=1024
  offline=512
  serviceRequested=256

  inputTrayMissing=128
  outputTrayMissing=64
  markerSupplyMissing=32
  outputNearFull=16
  outputFull=8
  inputTrayEmpty=4
  overduePreventMaint=2
  }

My python version uses subprocess for the snmpwalk and does the formatting more or less as above. Then:

# A dictionary of the errors and bit places
errors = {
    16: "low paper",
    15: "no paper",
    ...etc

# a still very ugly bit parse starting with a hex str like '2A00':
b = bin(int(hexstr, 16))[2:] # hex to int, int to bin, cut off the '0b'
binstr = str(b)
length = len(binstr)
indices = []
for i, '1' in enumerate(binstr):
     # find negative index of all '1's and multiply by -1
     # a hack to get around the binary string not containing leading zeros
     indices.append((i-length)*-1)

Then just compare the indices to the errors dictionary and you're done.

Anyway, very ugly, probably pretty inefficient. What is a more python-y, high-level, readable way to accomplish the same?


Solution

  • Given that you can parse your string as such:

    flags = int(hexstr, 16)
    

    I think you're looking for

    flag_indices = []
    for flag in errors:
        if flags & (2**flag):
            flag_indices.append(flag)
    

    Instead of building the list and indexing it, you can directly do

    (flags >> flag) & 1
    

    on the integer which will be a damn lot faster.