python 2和3之间的“地图”功能问题

时间:2019-04-08 02:06:39

标签: python-3.x dictionary ctypes converters

我正在使用别人编写的代码来读取和编码来自数字/模拟转换器的数据。我尝试通过使其与python 3一起使用来改进此代码,因为它是在python 2中编码的。

我已经减少了很多代码以了解为什么它在Python 3上不起作用,并发现了“ map”函数行为的问题,因为它说“在Python 3中发生了错误,但在python 2中没有发生。

我不知道我在哪里做错了,因为我自己没有编写此代码,因此有些地方我无法理解。 我应该进行哪些更改才能使其与python 3兼容?


from DTOL import DTOL

io = DTOL(name='DT9818(00)')

io.Initialize('DT9818(00)')
io.setupGetSingleValue()

DTOL


import ctypes
from time import sleep
from DTOL_defs import *
from DTOL_prototypes import *

class DTOL:
    """ Data Translation DtOLWrapper Class """
    def __init__(self, name='DT9818(00)'):
        self.name = name
        self.data = []
        self.sshandle = []
        self.hdev = []
        self.range = (-10,10)
        self.rmin = ctypes.c_double(-10)
        self.rmax = ctypes.c_double(10)
        self.gain = ctypes.c_double(1)
        self.res = ctypes.c_uint(16)
        self.enc = ctypes.c_uint(OL_ENC_BINARY)
        self.val = 0

    def Initialize(self, name):
        print('Available Boards:')
        olDaEnumBoards(listboardscallback, 1)
        print('-----')
        print('Initializing: ' + str(name))
        name = name.encode('utf-8')
        hdev = olDaInitialize(name)
        self.hdev = hdev
        return hdev

    def GetSubsystem(self, subsystem_code, elemNum):
        print(subsystem_code)
        print(elemNum)
        sshandle = olDaGetDASS(self.hdev, subsystem_code, elemNum)
        self.sshandle = sshandle
        return sshandle

    def setupGetSingleValue(self):
        self.Initialize(self.name)
        self.GetSubsystem(OLSS_AD, ctypes.c_ulong(0))


if __name__ == "__main__":
    print("Usage: io=DTOL('DT9818(00)')")
    print("io.setupSingleValue()")
    print("io.getSingleValue()")
    io = DTOL()
    io.setupGetSingleValue()

DTOL_prototypes


import ctypes
from ctypes.util import find_library

dll = ctypes.CDLL(find_library('oldaapi64'))
dll2 = ctypes.CDLL(find_library('OLMEM64'))

def errcheck_all(ret, func, args):
    if ret:
        print("Error occured in"+ str(func))
        return

    return args

def errcheck_none(ret, func, args):
    if ret:
        print("Error occured in"+ str(func))
        print(ret)
        return

# ----------- Initialize ---------------------------------
prototype = ctypes.WINFUNCTYPE(ctypes.c_int, ctypes.c_char_p, ctypes.POINTER(ctypes.c_ulong))
paramflags = (1, "name"), (2,"hDev")
olDaInitialize = prototype(('olDaInitialize', dll), paramflags)
olDaInitialize.errcheck = errcheck_all
# -----------END Initialize ---------------------------------

# ----------- olDaGetDASS ---------------------------------
prototype = ctypes.WINFUNCTYPE(ctypes.c_int, ctypes.c_ulong, ctypes.c_long, ctypes.c_uint, ctypes.POINTER(ctypes.c_ulong))
paramflags = (1, "hDev"), (1,"SubsystemType"), (1,"uiElementNr"), (2,"adhandle")
olDaGetDASS = prototype(('olDaGetDASS',dll), paramflags)
olDaGetDASS.errcheck = errcheck_all
# ----------- olDaGetDASS ---------------------------------

DTOL_defs


import ctypes

(OLSS_AD, 
OLSS_DA, 
OLSS_DIN, 
OLSS_DOUT, 
OLSS_SRL, 
OLSS_CT) = list(map(ctypes.c_int, (range(6))))

OL_ENC_BINARY = 200

结果,我发现了python 3和2之间的区别: Python 3:

c_long(0)
<class 'ctypes.c_long'>
Available Boards:
Name = b'DT9818(00)'
Drivername = b'Dt9818'
1
-----
Initializing: DT9818(00)
Available Boards:
Name = b'DT9818(00)'
Drivername = b'Dt9818'
1
-----
Initializing: DT9818(00)
c_long(0)
c_ulong(0)
Error occured in<WinFunctionType object at 0x00000235269DC528>

Python 2:

c_long(0)
<class 'ctypes.c_long'>
Available Boards:
Name = DT9818(00)
Drivername = Dt9818
1
-----
Initializing: DT9818(00)
Available Boards:
Name = DT9818(00)
Drivername = Dt9818
1
-----
Initializing: DT9818(00)
c_long(0)
c_ulong(0L)

最后两个参数分别对应于“ print(subsystem_code)”和“ print(elemNum)”。

2 个答案:

答案 0 :(得分:1)

我不知道您为什么认为 map 是问题。在两个 Python 版本中,它的调用方式(从中构造 list )均相同。

发布[Python 3]: ctypes - A foreign function library for Python

如果没有函数文档,就无法确定 100%,但是从声明它们的方式来看:

  1. olDaInitialize

    prototype = ctypes.WINFUNCTYPE(ctypes.c_int, ctypes.c_char_p, ctypes.POINTER(ctypes.c_ulong))
    paramflags = (1, "name"), (2, "hDev")
    
  2. olDaGetDASS

    prototype = ctypes.WINFUNCTYPE(ctypes.c_int, ctypes.c_ulong, ctypes.c_long, ctypes.c_uint, ctypes.POINTER(ctypes.c_ulong))
    paramflags = (1, "hDev"), (1, "SubsystemType"), (1, "uiElementNr"), (2, "adhandle")
    

我可以得出结论,返回值代表函数的执行状态,通常是:

  • 0 成功
  • 任何其他表示错误代码的 int

但是,在 Initialize 中,设置 hDev (这是 olDaInitialize 的输出,是 olDaGetDASS 的输入)像这样(也有类型不匹配,但被自动强制转换隐藏了):

hdev = olDaInitialize(name)
self.hdev = hdev

因此您将 olDaInitialize 的状态分配给 hdev ,这毫无意义。正确的方法是:

  • __ init __ 中:

    self.hdev = ctypes.c_ulong(0)
    
  • 初始化中:

    hdev = ctypes.c_ulong(0)
    status = olDaInitialize(name, ctypes.byref(hdev))
    if status == 0:  # Or whichever value means SUCCESS
        self.hdev = hdev
    

完全相同的内容也适用于 adhandle olDaGetDASS 的最后一个参数),您在 GetSubsystem中引用(如 shangdle ),并且可能在代码中使用其他地方。

我们处于改进部分:

  • 同时传递给 __ init __ Initialize 名称似乎是多余的。我建议将其保留在初始化程序中,并放在正文中:

    self.name = name.encode("utf-8")
    

    ,然后从 Initialize 中删除参数,并在其中使用self.name

  • 您正在使用 errcheck 功能,但是现在它根本没有帮助。您可以对此进行改进(例如,通过显示错误代码)。有关[SO]: How to use ctypes' errcheck? (@CristiFati's answer)

  • 的更多详细信息
  • Initialize 被调用两次:在主脚本和 setupGetSingleValue 中。您应该从一个地方删除呼叫(我想说是后者)

  • olDaEnumBoards (和 listboardscallback )丢失。另外, 1 st 2行是从哪里打印的?

  • 尝试使标识符名称保持一致([Python]: PEP 8 -- Style Guide for Python Code

@ EDIT0

添加一些简化的代码,以检查问题是否重现。

code.py

#!/usr/bin/env python3

import sys
import ctypes


def main():
    oldaapi64 = ctypes.CDLL(find_library("oldaapi64"))
    olmem64 = ctypes.CDLL(find_library("olmem64"))

    oldainitialize = oldaapi64.olDaInitialize
    oldainitialize.argtypes = [
        ctypes.c_char_p, ctypes.POINTER(ctypes.c_ulong),
    ]
    oldainitialize.restype = ctypes.c_int

    oldagetdass = olmem64.olDaGetDASS
    oldagetdass.argtypes = [
        ctypes.c_ulong, ctypes.c_long, ctypes.c_uint, ctypes.POINTER(ctypes.c_ulong),
    ]
    oldagetdass.restype = ctypes.c_int

    dev = ctypes.c_ulong(0)
    res = oldainitialize(b"DT9818(00)", ctypes.byref(dev))
    print("{:s} returned {:d}. Dev: {:d}".format(oldainitialize.name, res, dev.value))

    OLSS_AD = 0
    element_num = 0
    handle = ctypes.c_ulong(0)
    res = oldagetdass(dev, OLSS_AD, element_num, ctypes.byref(handle))
    print("{:s} returned {:d}. Handle: {:d}".format(oldagetdass.name, res, handle.value))


if __name__ == "__main__":
    print("Python {:s} on {:s}\n".format(sys.version, sys.platform))
    main()
    print("Done.")

答案 1 :(得分:0)

我还尝试使用类似的ADC与python 3.x一起使用。和您一样,我遇到了与python 2.7一起运行的pyDTOL模块。不幸的是,我陷入了同样的问题。 我试图了解pyDTOL中到底发生了什么,最终得到了与CristiFati类似的方法。我添加了发布ADC的功能。 这是我的代码(适用于python 2.7,但对于3.x有问题):

import ctypes
from ctypes.util import find_library

def main():
    ctypes.cdll.LoadLibrary(find_library('oldaapi64'))  
    oldaapi = ctypes.CDLL("oldaapi64") 

    name= b'DT9836S(00)'

    olDaInitialize = oldaapi.olDaInitialize
    olDaInitialize.argtypes = [ctypes.c_char_p, ctypes.POINTER(ctypes.c_ulong)]
    olDaInitialize.retype = ctypes.c_ulong

    hdev = ctypes.c_ulong()
    ret_init = olDaInitialize(name, ctypes.byref(hdev))
    print('hdev: ' + str(hdev))
    print('return from olDaInitialize: ' + str(ret_init))

    olDaGetDASS = oldaapi.olDaGetDASS
    olDaGetDASS.argtypes = [ctypes.c_ulong, ctypes.c_long, ctypes.c_uint, 
                            ctypes.POINTER(ctypes.c_ulong)]
    olDaGetDASS.restype = ctypes.c_ulong

    OLSS_AD = ctypes.c_long(0)
    sshandle = ctypes.c_ulong()
    ret_dass = olDaGetDASS(hdev.value, OLSS_AD, ctypes.c_uint(0), 
                           ctypes.byref(sshandle))
    print('sshandle: ' + str(sshandle))
    print('return from olDaGetDASS: ' + str(ret_dass))

    olDaReleaseDASS = oldaapi.olDaReleaseDASS
    olDaReleaseDASS.argtype = ctypes.c_ulong
    olDaReleaseDASS.restype = ctypes.c_ulong

    ret_rdass = olDaReleaseDASS(sshandle.value)

    olDaTerminate = oldaapi.olDaTerminate
    olDaTerminate.argtype = ctypes.c_ulong
    olDaTerminate.restype = ctypes.c_ulong

    ret_term = olDaTerminate(hdev.value)


if __name__ == "__main__":
    main()

Python 2.7中的输出:

hdev: c_ulong(11836704L)
return from olDaInitialize: 0
sshandle: c_ulong(11843088L)
return from olDaGetDASS: 0

Python 3.6中的输出:

hdev: c_ulong(3420637248)
return from olDaInitialize: 0
sshandle: c_ulong(0)
return from olDaGetDASS: 39

根据“ oldaapi64-手册”,返回值“ 0”表示“操作已完成;没有错误。 ” olDaGetDass的重新运行值为39表示:“指定了非法的设备句柄。 “

在我看来,有两种可能性: hdev指针(来自“ olDaInitialize”)在python 3中有不同的解释,或者将hdev传递给“ olDaGetDASS”会出现问题。

如果我们能找到解决方法,那就太好了!

修改: @来自CrisiFati的答复:DLL olmem64没有功能 olDaGetDASS

oldagetdass = olmem64.olDaGetDASS

您必须使用oldaapi64

oldagetdass = oldaapi64.olDaGetDASS

用户手册可以在这里找到:

https://www.mccdaq.com/PDFs/Manuals/UMDataAcq.pdf 附录A显示了一个示例。 在 Calling Conventions 中,声明返回值是无符号长且使用Microsoft Pascal调用约定。

最好的问候