dayjaby / zebra-scanner

Read barcodes in python with zebra barcode scanner
MIT License
24 stars 18 forks source link

Support for setting attribute values in the scanner #15

Closed Sthing closed 4 years ago

Sthing commented 4 years ago

Hi @dayjaby.

This is perhaps not the way it should be done... I can see that you have planned for updates to attribute values to be done in

void Attribute::set_value(py::object v) {
    ///TODO: implement this
}

However I simply could not figure out how to access the value of the py::object from C++ (ie cast it back to a C type), so I gave up and implemented Scanner::SetAttribute() instead.

I hope you find it useful.

I use it from my code like this:

    def ensure_parameter_value(scanner, name, number, value):
        """
        Constantly writing persistent parameter values to the scanner will destroy it's EEPROM.
        So we check the current value before attempting an update.

        Parameters:
        scanner: zebra_scanner.Scanner
        name: str
        number: int
        """
        scanner.fetch_attributes(str(number))
        attribute = scanner.attributes.get(number)
        if attribute is None:
            logging.error(f"ZebraScanner: Could not fetch attribute #{number} from scanner #{scanner.scannerID}.")
            return
        if (attribute.value == value):
            return  # No need for update.

        logging.info("ZebraScanner: Scanner #{} parameter #{} ({}) is {!r}, should be {!r}. Updating...".format(
            scanner.scannerID,
            number,
            name,
            attribute.value,
            value
        ))
        result = scanner.set_attribute(str(number), attribute.datatype, str(value), persist = True)
        if result == 0:
            logging.info("ZebraScanner: Scanner #{} set_attribute('{}', '{}', '{}') returned OK.".format(
                scanner.scannerID,
                number,
                attribute.datatype,
                value
            ))
        else:
            logging.error("ZebraScanner: Scanner #{} set_attribute('{}', '{}', '{}') failed, error={}.".format(
                scanner.scannerID,
                number,
                attribute.datatype,
                value,
                result
            ))

    ensure_parameter_value(scanner, "Parameter Barcode Scanning", 236, True)
dayjaby commented 4 years ago

Is v.cast<bool>() and other versions of py::object::cast<T> not working?

Sthing commented 4 years ago

Thanks for the tip - casting like that works just fine! ;-)

However we need to decide on a way to choose between commands CMD_RSM_ATTR_SET and CMD_RSM_ATTR_STORE. CMD_RSM_ATTR_SET will set the attribute, but it will not persist across a reset or power cycling of the device. CMD_RSM_ATTR_STORE will set the attribute AND save the value to flash, so it persists across resets and reboots.

I do not like proposal 1 - it feels wrong. I would like to implement proposal 2.

What do you think @dayjaby?

dayjaby commented 4 years ago

Proposal 2 feels more elegant and intuitive. But I would vote for having either a property getter and setter, or none of both. So either attr.value = 1 or the functions set_value and get_value.

Sthing commented 4 years ago

New usage example:

def ensure_parameter_value(scanner, name, number, value):
    """
    Constantly writing persistent parameter values to the scanner will destroy it's EEPROM.
    So we check the current value before attempting an update.

    Parameters:
    scanner: zebra_scanner.Scanner
    name: str
    number: int
    """
    scanner.fetch_attributes(str(number))
    attribute = scanner.attributes.get(number)
    if attribute is None:
        logging.error(f"ZebraScanner: Could not fetch attribute #{number} from scanner #{scanner.scannerID}.")
        return
    if (attribute.value == value):
        return  # No need for update.

    logging.info("ZebraScanner: Scanner #{} parameter #{} ({}) is {!r}, should be {!r}. Updating...".format(
        scanner.scannerID,
        number,
        name,
        attribute.value,
        value
    ))
    result = attribute.store_value(value)
    if result == 0:
        logging.info("ZebraScanner: Scanner #{} attribute #{} store_value({}) returned OK.".format(
            scanner.scannerID,
            number,
            value
        ))
    else:
        logging.error("ZebraScanner: Scanner #{} attribute #{} store_value({}) failed, error={}.".format(
            scanner.scannerID,
            number,
            value,
            result
        ))

ensure_parameter_value(scanner, "Symbology: QR Code", 293, True)
Sthing commented 4 years ago

Hi @dayjaby. I have reimplemented attribute setting. Is this usable?

/thing

dayjaby commented 4 years ago

LGTM! Thanks