Page 1 of 1

Bus status evaluation of multiple channels

Posted: Mon 12. Aug 2024, 14:46
by florianber2
Maybe I misunderstand the conecept but the function GetCanBusStatus fails to retrieve the status of adapter #2 when initialized adapter #1 is unplugged. I use the UDS-API - the binding for Python and have installed the latest drivers 4.9.0.942. The version - read with GetValue_2013 - are:

Code: Select all

UDS API verison 2.3.1.295
ISO TP API verison 3.5.0.344
PCAN Basic API verison 4.9.0.942
I compiled a minimum example for Windows 10/11 that I tried out with two PCAN-USB adapters (IPEH-002022). The python script initializes adapter #1 (=device-id) and then enters a infinte loop to enumerate any adapters conected to the PC. To reproduce the problem:
  • Connect two adapters to a Windows PC
  • Run the script, in VSCode
  • Initialization of adapter #1 should succeed (line "All Ok" in terminal)
  • Enumeration should print "Adapter 1 BELEGT, all OK; Adapter 2 FREI, not initialized
  • Unplug adapter 1 which is initialized
  • I expected output: Adapter 2 FREI not initialized; but received output: Adapter 2 FREI Invalid cantp_handle
  • Plug in adapter 1 again
  • Enumeration prints idential (1 all OK, 2 not initialized)
  • Click-focus terminal window, then press CTRL + C to exit infinte loop gracefully.
Is this behavior intended? Have I misinterpreted some details in the docu/manual? To be precise: I intend to enumerate the status and condition of all connected adapters. I know that adapters move down uninitialized channels whenever one adapter is unplugged (i.e. Device "2" assigned but not initalized to channel CANBUS2 move to CANBUS1 when Device "1" is unplugged. What I did not expected was that the state of "2" is influenced by the state of initialized "1", especially when "1" is unplugged - rather BUS1 should have been reserved for "1" until uninitialize is called on "1" or adapter "1" is plugged back in again.

Here is the minimum working example EDIT: I forgot two helper to_str functions:

Code: Select all

#peak_adapter_enumeration.py - Python 3.11 - Windows 10 Enterprise 22H2 19045.4529
import time
import ctypes
import PCAN_UDS_2013 as puds 

if __name__ == "__main__":

     def adapter_to_str(adapter_handle : puds.TPCANHandle, include_index : bool = False):
        """returns readable name 'PCAN_USBBUS###' of specified handle"""

        if adapter_handle is None:
            return "NONE_SELECTED"

        if isinstance(adapter_handle, puds.TPCANHandle):
            adapter_handle = adapter_handle.value
        # 1-8 [gap] [9-16] -> I hate it...
        if adapter_handle == 0:
            return "PCAN_NONEBUS"
            # raise ValueError(f"Specified handle is zero due to a) contact issue (replug USB connector of adapter), b) wrong assignment (zero or PCAN_NONEBUS cannot be parsed)")
        elif adapter_handle < puds.PCAN_USBBUS1.value:
            raise ValueError(f"TPCANHandle, parsing error: invalid handle < PCAN_USBBUS1 (raw={adapter_handle}, hex={hex(adapter_handle)}).")
        elif adapter_handle >= puds.PCAN_USBBUS1.value and adapter_handle <= puds.PCAN_USBBUS8.value:
            return f"PCAN_USBBUS{adapter_handle - puds.PCAN_USBBUS1.value + 1}" + (f"({adapter_handle})" if include_index else "")
        elif adapter_handle > puds.PCAN_USBBUS8.value and adapter_handle < puds.PCAN_USBBUS9.value:
            raise ValueError(f"TPCANHandle, parsing error: WARNING, DEV(!): handles are not sequential, there is a gap between 1-8 and 9-16. use the PUDS_USBBUSxx constants or be careful.  (raw={adapter_handle}, hex={hex(adapter_handle)}).")
        elif adapter_handle >= puds.PCAN_USBBUS9.value and adapter_handle <= puds.PCAN_USBBUS16.value:
            return f"PCAN_USBBUS{adapter_handle - puds.PCAN_USBBUS9.value + 9}" + (f"({adapter_handle})" if include_index else "")
        else:
            raise ValueError(f"TPCANHandle, parsing error: invalid handle > PCAN_USBBUS16 (raw={adapter_handle}, hex={hex(adapter_handle)}).")
        

    def str_to_adapter(name : str):
        """returns TPCANHandle corresponding to readable string. Use together with ``generate_peak_adapter_string_list``"""

        NUM_INTERFACE_CHANNELS = 16

        try:

            offset = int(name.split("PCAN_USBBUS")[1])

            if offset < 1 or offset > NUM_INTERFACE_CHANNELS:
                raise Exception(f"Cannot return TPCANHandle, invalid syntax. '{name} interface {offset} out of bounds 0 < x <= 16")


            return puds.TPCANHandle(puds.PCAN_USBBUS1.value + offset - 1)
        except (IndexError, ValueError) as iex:
            raise Exception(f"Cannot return TPCANHandle, invalid syntax. '{name}' must begin with PCAN_USBBUS, and end with an integer value") from iex

    def create_uds_tmp_buffer(template : Type, count : int) -> Any:
        """"creates a temporary buffer of N structures (see PCAN_UDS_2013.py) that can be passed to GetValue_2013(). 
        HINT: pass this buffer using ctypes' byref() method!"""
        return (template * count)()

    def enum_active_channels():

        # use API function to get numer of active channels

        nActiveChannels = ctypes.c_uint32()
        uds.GetValue_2013(puds.PCAN_NONEBUS, puds.PCAN_ATTACHED_CHANNELS_COUNT, nActiveChannels, ctypes.sizeof(nActiveChannels))

        # use API function to enumerate channels with detected adapters
        activeDevices = create_uds_tmp_buffer(puds.TPCANChannelInformation, nActiveChannels.value)
        retStatus = uds.GetValue_2013(puds.PCAN_NONEBUS, puds.PCAN_ATTACHED_CHANNELS, activeDevices, ctypes.sizeof(activeDevices))

        return activeDevices

    def get_error_text(status_in : puds.TPCANStatus):     
            
            tmpBuf = ctypes.create_string_buffer(256)
            status =  uds.GetErrorText_2013(status_in, 0, tmpBuf, ctypes.sizeof(tmpBuf)) # return c_type.c_long

            return tmpBuf.value.decode("latin-1", errors='replace')
    
    def get_api_version(api_parameter : puds.uds_parameter):     
            
            tmpBuf = ctypes.create_string_buffer(256)
            status =  uds.GetValue_2013(puds.PCAN_NONEBUS, api_parameter, tmpBuf, ctypes.sizeof(tmpBuf)) # return c_type.c_long

            return tmpBuf.value.decode("latin-1", errors='replace')

    #### START ####
    uds = puds.PCAN_UDS_2013()
    
    print("UDS API verison " + get_api_version(puds.PUDS_PARAMETER_API_VERSION))
    print("ISO TP API verison " + get_api_version(puds.PCANTP_PARAMETER_API_VERSION))
    print("PCAN Basic API verison " + get_api_version(puds.PCAN_API_VERSION))
    print("###")
    print(get_error_text(uds.Initialize_2013(puds.PCAN_USBBUS1, puds.PCAN_BAUD_500K)))

    time.sleep(1.0)

    try:
        while True:

            for ch in enum_active_channels():
                bus_status = uds.GetCanBusStatus_2013(ch.channel_handle)
                status_text = get_error_text(bus_status)
                
                print(f"{adapter_to_str(ch.channel_handle)} - ID: {ch.device_id} - {channel_condition_to_str(ch.channel_condition)} - {status_text}")
            pass
            print("###########")

            time.sleep(0.25)
            

    
    except KeyboardInterrupt:
        # click-focus "TERMINAL"-window in VSCode, then press CTRL + C
        uds.Uninitialize_2013(puds.PCAN_NONEBUS)

    print("END")

Re: Bus status evaluation of multiple channels

Posted: Mon 12. Aug 2024, 15:34
by M.Heidemann
Hello,

You are missing one crucial thing: the handles are given out dynamically, depending on available interfaces.

Once you unplug interface #1 handle #2 doesnt exist anymore, and yes, that's the intended behavior.

So calling it upon an non-existing handle yields the results you are seeing.

You may have better luck by fixing the handles to an device-id first, so the handles are not dynamically allocated.

Please see this forum thread regarding this:

viewtopic.php?f=120&t=125


Best Regards

Marvin

Re: Bus status evaluation of multiple channels

Posted: Tue 13. Aug 2024, 08:30
by florianber2
Hello and thank you for the link.

I played around with the registry but still end up with status INVALID_HANDLE. What concerns me, is that the results from channel enumeration (TPChanChannelInformation.channel_condition > ATTACHED_CHANNNELS) differ from (acutal) CHANNEL_CONDITION of handle USBBUS1 and USBBUS2 (CHANNEL_CONDITION).

I added the line (and local function) to my test script to manually query conditions of channel USBBUS1 and USBBUS2.

Code: Select all

def get_condition(ch : puds.TPCANHandle):
        cond = ctypes.c_uint32()
        status = uds.GetValue_2013(ch, puds.PUDS_PARAMETER_CHANNEL_CONDITION, cond, ctypes.sizeof(cond))
        return cond
print(f"Conditions, PCAN_USBBUS1 : {channel_condition_to_str(get_condition(puds.PCAN_USBBUS1).value)}; PCAN_USBBUS2: {channel_condition_to_str(get_condition(puds.PCAN_USBBUS2).value)}")
As expected, when two adapters are plugged in (ID: 1 connected), conditions (BELEGT, FREI, N/A) match:
PCAN_USBBUS1 - ID: 1 - ADAPTER BELEGT - All OK
PCAN_USBBUS2 - ID: 2 - ADAPTER FREI - Not Initialized
Conditions, PCAN_USBBUS1: ADAPTER BELEGT; PCAN_USBBUS2: ADAPTER FREI
What concerns me are the conflicting channel conditions, once I unplug the first adapter with ID 1:
PCAN_USBBUS1 - ID: 2 - ADAPTER FREI - Invalid cantp_handle
Conditions, PCAN_USBBUS1: ADAPTER BELEGT; PCAN_USBBUS2: KEIN ADAPTER, N/A
Why does the CHANNEL_CONDITION not equal to ATTACHED_CHANNELS TPCANChannelInformation.channel_condition?
Why has adapter ID2 the status "invalid cantp_handle" once it is dynamically reassigned to USBBUS1, instead of status "not initialized" as before? Is this because adapter #1 was and is still initialized on USBBUS1 at this point?

Unrelated to this issue, I will try to work with the static assignment through registry or adhere to device_ids in code (e.g. CAN_LookupChannel) directly.

Re: Bus status evaluation of multiple channels

Posted: Tue 13. Aug 2024, 09:16
by M.Heidemann
Hello,

This actually clears up the concern you have with this, thank you for elaborating.

I'll have to ask our software development team for some insight on this because i personally
don't have enough information on the logic behind these parameters to give a conclusive answer.

Let me contact them with your case and get back to you, if that's fine with you?

BR

Marv

Re: Bus status evaluation of multiple channels

Posted: Tue 13. Aug 2024, 09:38
by florianber2
Yes, of course, if there was an issue, the devs would probably know what to do firsthand. I will find a workaround in the meantime.

Thank you, and BR,
Florian.

Re: Bus status evaluation of multiple channels

Posted: Tue 13. Aug 2024, 11:07
by M.Heidemann
Hello,

I have tested this myself, not being able to replicate this behavior.
In my case i'll get "Hardware Unavailable" which i would consider correct,
the handle will not pop up on "channels attached" anymore.

Maybe check your version while you're running the app:

Code: Select all


stsResult = self.m_objPCANBasic.GetValue(PCAN_NONEBUS, PCAN_API_VERSION)
        
        if stsResult[0] == PCAN_ERROR_OK:
            print("-----------------------------------------------------------------------------------------")
            print(f"PCAN API Version: {stsResult[1]}")
            print("")
        else:
            self.ShowStatus(stsResult)
            
Let's make sure we are running the same version!

BR

Marvin