• 技术博客

    18/05/2021 作者 云海游戏

    怎样连接到特定的云海游戏 CAN通道

    在此博客中,我将展示怎样通过昵称而不是CANlib通道号,连接到特定的一路云海游戏 CAN通道。此方法可实现让一路CAN通道为指定接口的专用通道。

    1. 自定义通道名称

    用户常常问我们 (support@kvaser.com)的一个问题是 :
    ? 我是否能让我的电脑总是以相同的顺序启动我的云海游戏接口?
    很遗憾,对这个问题的回答是否定的。

    通常,如果你有多个接口,那么每次都会以相同的顺序启动这些接口,但是如果你修改了一个接口,那么顺序就会不同。

    ? 是否可以用标识符命名每个通道?
    是的,可以为每路通道设置一个名称,以标识通道。
    (将在稍后另一个博客中解释)

    有一个非常简单的方法,不需要复杂的操作,就可以很容易打开一路特定的CAN通道。我们所有的(云海游戏)接口都有一个产品代码(EAN)和一个序列号,再加上“卡上的通道号”,这三个数据字段创建了一个独特的组合。

    例子

    我的“云海游戏 USBcan Pro 2xHS v2”接口有两路通道, 0 和1, 它们在CANlib中显示为1和2。

    2. 查找一路特定通道的通道信息

    如果我们查看我的“云海游戏 USBcan Pro 2xHS v2”接口, 我们会看到:
    设备 EAN 73-30130-00752-9 (这是产品系列号)
    系列号 12160
    卡上通道 0 (在此第一路通道上)
    卡上通道 1 (在此第二路通道上)

    如果我基于此信息创建一个名称,例如“00752-9:12160/1”,那么此名称将始终指向我的“USBcan Pro”的第二路通道,没有其他云海游戏接口具备此组合。

    因此,如果我已将“00752-9:12160/1”连接到一个高度机密的测试对象,那么我就知道当我打开该名称组合,我将始终与该测试对象通信,同时我并不需要了解我的电脑中安装了多少个其他接口。这类似于canOpenChannel(‘00752-9:12160/1’,flags)。

    2.1. CANlib命令canOpenChannel()

    通常,当我们调用命令canOpenChannel时,我们会提供一个CANlib通道号和一些FLAG(标志)。

    我们可以改用canOpenChannel(‘00752-9:12160:1’,flags)句式吗?

    实际上,在最理想的情况下,这个命令应该已经是CANlib的一部分了,但是它还尚不存在,所以你必须自己去做。

    2.2. 用户自定义的MycanOpenChannel()

    让我们创建一个命令: MyCanOpenSpecificChannel()

    Delphi
    function MyCanOpenSpecificChannel(const NickName: string; const flags: integer; var FoundChannel: integer): canHandle;

    Parameters(参数)
    [in] NickName String with “EAN:SN/ChOnCard” (‘00752-9:12160/1’)
    (带有“EAN:SN/ChOnCard”的昵称字符串)
    [in] flags A combination of canOPEN_xxx flags (一个canOPEN_xxx的组合)
    [out] FoundChannel(找到的通道) The number of the opened channel(已打开的通道数)

    Returns(返回)
    返回一个给断开回路的句柄,或者,如果调用失败,则返回canERR_xxx (负)。

    完整的MyCanOpenSpecificChannel() 函数目录请见附录A。

    我们假设昵称包含‘00752-9:12160/1’。让我们调用函数SplitNickNameStringPlease(), 它将返回EAN、SN和NoOnCard。

    EAN = ‘00752-9’
    SN = 12160
    NoOnCard = 1

    通过canGetChannelData()命令,我们可以获取所连接相关接口的大量信息。我们将使用参数:canCHANNELDATA_CARD_SERIAL_NO, canCHANNELDATA_CHAN_NO_ON_CARD 和 canCHANNELDATA_CARD_UPC_NO。

    canCHANNELDATA_CARD_SERIAL_NO uses a 64bit integer
    canCHANNELDATA_CHAN_NO_ON_CARD uses a 32bit integer
    canCHANNELDATA_CARD_UPC_NO uses a 64bit BCD string

    所以通过调用 (当迭代通过所有通道时 (I)):
    canGetChannelData(I, canCHANNELDATA_CARD_SERIAL_NO , U64_1, sizeof(U64_1));
    canGetChannelData(I, canCHANNELDATA_CHAN_NO_ON_CARD, U32_1, sizeof(U32_1));
    canGetChannelData(I, canCHANNELDATA_CARD_UPC_NO , U64_2, sizeof(U64_2));

    我们将在U64_1,U32_1 和 U64_2中收到一些信息。

    U64_1 and U32_1将包含卡上相应通道号的系列号,并且我们可以很容易地将它与我们的搜索值进行比较。

    U64_2包含EAN编号,我们需要做一些处理才能读取EAN编号。

    首先,将U64_2转换为16位长的十六进制字符串,
    2026405030294825 ToHexString变为: ‘0007330130007529’

    完整的EAN代码是73-30130-00752-9,所以很容易找到我们要复制的内容
    73-30130-00752-9 ‘0007330130007529

    st := copy(st, 11, 5) + ‘-‘ + copy(st, 16, 1);

    我们在寻找状况:
    EAN = ‘00752-9’
    SN = 12160
    NoOnCard = 1

    如果 (U64_1=SN) 和 (U32_1=SN) 以及 (EAN=st),那么我们已经找到了我们的接口, 让我们返回此通道号并尝试调用 canOpenChannel()。

    你可以在附录A中看到函数的完整列表。

    3. 结论

    当然,你几乎可以选择任何格式来描述某个特定通道。

    我在此博客中要解释的是,通过三次调用canGetChannelData(),你将收到足够的信息来确定,是否它就是你要打开的某个通道。

    我希望本文会对你有帮助。欢迎评论和提问!

    4. 附录A :MycanOpenChannel()

    4.1. 函数MycanOpenChannel()

    function MycanOpenChannel(const NickName: string; const flags: integer; var FoundChannel: integer): canHandle;
        var
          I: integer;
    
          EAN       : string;
          SN        : UINT64;
          ChNoOnCard: UINT32;
    
          N: integer;
          UI64   : UINT64;
          UI32   : UINT32;
          st     : string;
    
        begin
          result       := canERR_NOTFOUND;
          FoundChannel := canERR_NOTFOUND;
    
          /// Most probably, NickName contains something like:'00752-9:12160:0'
          /// EAN=00752-9 (this is the last part of Device EAN	73-30130-00752-9)
          /// sn = 12160
          /// NoOnCard=0
    
          if SplitNickNameStringPlease(NickName, EAN, SN, ChNoOnCard) then
          begin
            // Let us see if we can find the combination of EAN, SN, ChNoOnCard
    
            if (canGetNumberOfChannels(N) = canOK) then
            begin
              for I := 0 to N - 1 do
              begin
                // Look for SN first
                if (canGetChannelData(I, canCHANNELDATA_CARD_SERIAL_NO, UI64, sizeof(UI64)) = canOK) then
                begin
                  if (UI64 = SN) then
                  begin
                    if (canGetChannelData(I, canCHANNELDATA_CHAN_NO_ON_CARD, UI32, sizeof(UI32)) = canOK) then
                    begin
                      if (UI32 = ChNoOnCard) then
                      begin
                        // We have found a channel with SN and NoC that is correct, let see if EAN is correct
                        if (canGetChannelData(I, canCHANNELDATA_CARD_UPC_NO, UI64, sizeof(UI64)) = canOK) then
                        begin
                          st := UI64.ToHexString(16);
                          st := copy(st, 11, 5) + '-' + copy(st, 16, 1);
                          if (st = EAN) then
                          begin
                            FoundChannel := I;
                            break; // Breaks the FOR-loop
                          end;
                        end;
                      end;
                    end;
                  end;
                end;
              end;
    
              if FoundChannel >= 0 then
              begin
                // Let us use "FoundChannel" and see what happens
                result := canOpenChannel(FoundChannel, flags);
              end;
            end;
          end;
        End;
    

    4.2. 函数 SplitNickNameStringPlease()

    function SplitNickNameStringPlease(const NickName: string; var EAN: string; var SN: UINT64; var NoC: UINT32): boolean;
        var
          P   : integer;
          st  : string;
          temp: string;
        begin
          result := false;
          try
            // '00752-9:12160/0'
            temp := NickName;
            P    := pos(':', temp);
            if P > 0 then
            begin
              EAN  := copy(temp, 1, P - 1);
              temp := copy(temp, P + 1, 1024);
    
              EAN := trim(EAN); // remove leading and trailing spaces
              while length(EAN) < 7 do
              begin
                EAN := '0' + EAN; // add leading zeros if needed
              end;
    
              P := pos('/', temp);
              if P > 0 then
              Begin
                st := copy(temp, 1, P - 1);
                SN := st.ToInteger;
    
                st  := copy(temp, P + 1, 1024);
                NoC := st.ToInteger;
    
                result := true;
              end;
            end;
          except
            result := false;
          end;
        end;
    Author Image

    Lars-G?ran Fredriksson

    Lars-G?ran Fredriksson现任云海游戏 AB的现场应用工程师。他拥有地理信息系统(GIS)和遥感领域的背景,目前专注于将云海游戏开发团队的专业知识与终端用户的实际需求相结合。他对CAN技术的热情可见一斑:入职首周,他便设计了一款互动式CAN知识问答游戏,引发了办公室全员寻找正确答案的热潮。此外,他还是一位对环保捕鱼充满热情的渔民,致力于研发新型环保捕鱼技术。目前,他放生的最大鱼种是一尾重约325公斤(715磅)的蓝鳍金枪鱼。

    【网站地图】【sitemap】