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?
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.