zoukankan      html  css  js  c++  java
  • 用c#实现单链表(程序代码已经验证,完全正确)

     

    C#开发EyeLink眼动仪的实验程序

     

     

     

    【题外话】

    Eyelink眼动仪是SR Research推出的一款眼动仪,很多高校都在使用其做实验。其官方提供了COM的接口,所以支持COM接口的开发平台都可以开发使用。官方甚至提供了一个C#的样例供参考,不过这个样例相比起其他的VC++的样例而言功能过于简单,程序本身也比较乱,再加上国内关于EyeLink的资料又比较少,所以这里我简要写下我使用EyeLink眼动仪的开发框架,方便大家开发。

    本文地址:http://www.cnblogs.com/mayswind/p/3417211.html

    【文章索引】

    1. 准备工作
    2. EyeLink实验的操作流程
    3. EyeLink的基本使用方法
    4. EyeLink的高级使用方法

    【一、准备工作】

    虽然SR Research提供的大多数样例都是基于VC++的,但由于C#开发快速高效,同时在实现复杂功能时C#更容易开发,所以我还是选择了使用C#开发实验程序,而不是VC++。SR Research比较有意思的是,官方虽然提供了所有的说明文档以及开发工具包,但是这些都需要从官网的论坛才可以下载,而且官网论坛的注册是需要人工审核的,所以注册一定千万不要着急。好在审核也不会慢,去除时差的原因没有多久就能通过审核。

    注册完成以后,首先需要下载开发工具包,该工具包里包括了SDK文件以及几个说明文档还有几个示例程序。地址见下:

    https://www.sr-support.com/showthread.php?6-Windows-Display-Software

    程序员开发手册(EyeLink Programmers Guide.pdf)就在安装后的目录中的Docs中,虽然是C语言版本的而且也不是使用的COM接口,但对于了解SDK还是有帮助的。当然也可以单独下载,地址见下:

    https://www.sr-support.com/showthread.php?4-EyeLink-C-Programmers-Guides

    所有的自带样例程序都在安装目录下的SampleExperiments目录,COM接口的都在com目录下,其中还有一个C#的样例程序。而EyeLink提供的COM接口文件位于Libs目录下,文件名为“SREyeLink.dll”,直接在C#里引用即可。

    【二、EyeLink实验的操作流程】

    一般使用EyeLink进行实验的都要遵循以下的流程:

    1、程序开始界面,可能介绍本实验的情况及提示接下来按什么键进入实验等等。

    2、一般正式进入实验流程后首先按C键进入Calibration界面进行眼睛的校准,一般为9点校准(可以自行设置),如果一个点校准失败会在所有点校准后重新校准。

    3、眼睛校准成功后按V键进入Validation界面进行刚才校准结果的验证。

    4、验证成功后开始实验,一个实验可能有多个Trail组成,在所有Trial之前或每个Trial之前可能需要Drift使受试者注视屏幕中心(位置可自定义)以继续。

    5、如果有多个Trial将依次执行。

    【三、EyeLink的基本使用方法】

    EyeLink提供的COM接口总体还是很友好的,常见的有以下几个类:

    1、EyeLink:与EyeLink操作主要的类,提供连接Host PC、创建EDF文件、向Host PC发送命令和向EDF中记录信息等等,常见的方法如下:

    方法名 含义
    void open("Host PC IP", 0); 连接到指定Host PC,Host PC IP通常为100.1.1.1
    void close(); 关闭指定连接
    void openDataFile("fileName"); 在Host PC创建指定文件名的EDF文件以供记录
    void receiveDataFile("fileName", "localPath"); 将Host PC记录的指定文件名的EDF传回到本地路径
    void startRecording(true, true, true, true); 开始记录注视点信息(四个选项可以设置是否记录指定内容)
    void stopRecording(); 结束记录注视点信息
    bool isRecording(); 获取是否正在记录注视点信息
    void sendCommand("command"); 向Host PC发送指定的指令
    void sendMessage("message"); 向Host PC发送指定的信息,记录在EDF文件中
    void doTrackerSetup(); 进入实验流程,允许用户按C和V执行相应操作或按Enter显示镜头内容
    void doDriftCorrect(x, y, true, true); 要求受试者看住指定的(x, y)点以继续
    void setOfflineMode(); Host PC进入Offline Mode,通常实验结束后要执行该操作
    short eyeAvailable(); 获取摄像头跟踪到的眼睛(可以转换为EL_EYE枚举) 
    short getLastButtonPress(out pressTime); 获取按键信息(比如可以获取连接在Host PC上的手柄的按键)
    Sample getNewestSample(); 获取注视点信息 

    2、EyeLinkUtil:提供了几个常用工具方法,如提供当前时间、进入实时模式等等。

    方法名 含义 
    void beginRealTimeMode(time); 开始指定时间(毫秒)的实时模式 
    void endRealTimeMode(); 结束实时模式 
    double currentTime(); 获取当前时间(毫秒) 
    ELGDICal getGDICal(); 获取EyeLink GDICal 

    3、ELGDICal:通过将窗口的Handle传给该类,实现由EyeLink SDK控制直接在界面上绘制Calibration、Validation以及Drift等界面,同时开发者也可以直接在界面线程上执行逻辑或绘制相应内容,而无需担心界面无响应而创建子线程等等。

    方法名 含义 
    void setCalibrationColors(foreColor, backColor); 设置Calibration、Validation、Drift等界面的前景色和背景色
    void setCalibrationWindow(hWnd); 设置上述界面的窗口Handle
    void enableKeyCollection(true/false); 设置是否允许通过键盘控制EyeLink SDK操作(如C、V、Enter)

    EyeLink基本使用非常简单,就如同给出的C#的样例一样,在这里给出一个简单的使用,比如直接在界面线程上执行代码(this这里就是指当前窗体):

    复制代码
     1 EyeLink eyeLink = new EyeLink();//创建EyeLink对象
     2 eyeLink.open("100.1.1.1", 0);//连接到Host PC
     3 eyeLink.openDataFile("test.edf");//在Host PC创建指定文件名的EDF以供记录
     4 eyeLink.sendCommand("file_event_filter = LEFT,RIGHT,FIXATION,SACCADE,BLINK,MESSAGE,BUTTON");//设置事件或记录的内容,下同
     5 eyeLink.sendCommand("file_sample_data = LEFT,RIGHT,GAZE,AREA,GAZERES,STATUS");
     6 eyeLink.sendCommand("link_event_filter = LEFT,RIGHT,FIXATION,SACCADE,BLINK,BUTTON");
     7 eyeLink.sendCommand("link_sample_data = LEFT,RIGHT,GAZE,GAZERES,AREA,STATUS");
     8 eyeLink.sendMessage(String.Format("DISPLAY_COORDS {0} {1} {2} {3}", this.Left, this.Top, this.Width, this.Height));//设置窗口的范围
     9 
    10 EyeLinkUtil eyeLinkUtil = new EyeLinkUtil();//创建EyeLinkUtil对象
    11 ELGDICal gdiCal = eyeLinkUtil.getGDICal();//创建EyeLinkGDICal对象
    12 
    13 gdiCal.setCalibrationWindow(this.Handle.ToInt32());//将窗体句柄传给EyeLink SDK
    14 gdiCal.enableKeyCollection(true);
    15 eyeLink.doTrackerSetup();//开始进行设置阶段(Calibration、Vadation等)
    16 gdiCal.enableKeyCollection(false);
    17 
    18 gdiCal.enableKeyCollection(true);
    19 eyeLink.doDriftCorrect((Int16)(this.Width / 2), (Int16)(this.Height / 2), true, true);//所有Trial前或每个Trial前让受试者注视指定位置以开始Trial
    20 gdiCal.enableKeyCollection(false);
    21 
    22 eyeLink.startRecording(true, true, true, true);//开始记录EDF文件
    23 Double st = eyeLinkUtil.currentTime();
    24 
    25 while ((st + 20000) > eyeLinkUtil.currentTime())//循环20秒程序结束
    26 {
    27     EL_EYE eyeUsed = (EL_EYE)eyeLink.eyeAvailable();//获取当前眼睛使用情况
    28     Sample sample = eyeLink.getNewestSample();//获取当前注视点
    29     
    30     if (sample == null || eyeUsed == EL_EYE.EL_EYE_NONE)
    31     {
    32         continue;
    33     }
    34     
    35     if (eyeUsed == EL_EYE.EL_BINOCULAR)
    36     {
    37         eyeUsed = EL_EYE.EL_LEFT;//如果两只眼睛同时被捕捉到则使用左眼
    38     }
    39     
    40     Single x = sample.get_gx(eyeUsed);//获取注视点相对窗口的横坐标
    41     Single y = sample.get_gy(eyeUsed);//获取注视点相对窗口的纵坐标
    42     Single pa = sample.get_pa(eyeUsed);//获取瞳孔大小
    43     
    44     if (x == (Single)EL_CONSTANT.EL_MISSING_DATA || y == (Single)EL_CONSTANT.EL_MISSING_DATA || pa <= 0)
    45     {
    46         continue;
    47     }
    48     
    49     //TODO: 在这里处理x、y坐标等
    50 }
    51 
    52 eyeLink.sendCommand("close_data_file");//关闭Host PC上的EDF文件
    53 eyeLink.receiveDataFile("test.edf", "本机文件路径");//将Host PC上的EDF文件传回到测试机
    54 
    55 eyeLink.setOfflineMode();//将Host PC设置为离线状态(非实验进行状态)
    56 eyeLink.stopRecording();//结束记录EDF文件
    57 eyeLink.close();//关闭与Host PC的连接
    58 
    59 eyeLink = null;
    60 eyeLinkUtil = null;
    复制代码

     

    【四、EyeLink的高级使用方法】

    有了上述这些还不够,比如:

    1、虽然上述我们能记录EDF文件,但是如果有多个Trial的话我们还希望能在EDF文件中予以区分。所以在每个Trial前后我们还需要这样去做:

    复制代码
    1 eyeLink.sendCommand(String.Format("record_status_message 'TRIAL {0}'", trialIndex));
    2 eyeLink.sendMessage(String.Format("TRIALID {0}", trialIndex));
    3 eyeLink.startRecording(true, true, true, true);
    4 
    5 //Trial逻辑
    6 
    7 eyeLink.stopRecording();
    复制代码

    2、如果我们在Trial中显示了图片,同时我们希望EDF Viewer在查看时也能将对应的图片显示出来,那么我们还需要在startRecording之前执行这样一句话,去让EDF Viewer打开指定路径下的图片。

    1 eyeLink.sendMessage(String.Format("!V IMGLOAD FILL {0}", imagePath));

    3、如果我们希望受测者能够使用按键进行Trial的操作(比如按某键表示完成或跳过该Trial之类的),我们还需要获取按键的信息,其中如果我们还需要在EDF文件中显示出按键的情况,我们还需要向Host PC发送一条信息(下列代码中第6行)。

    复制代码
    1 Double pressTime = 0;
    2 Int16 buttonCode = eyeLink.getLastButtonPress(out pressTime);
    3 
    4 if (buttonCode != 0)
    5 {
    6     eyeLink.sendMessage(String.Format("ENDBUTTON {0}", buttonCode));
    7     //TODO: 执行相关按键的逻辑
    8 }
    复制代码

    4、如果我们希望在Drift的过程中,受测者在将眼睛注视到指定的点后还需要按指定的键才能继续的话,我们还需要在开始的配置过程中写如下的代码(这里使用的是按键5):

    1 eyeLink.sendCommand("button_function 5 'accept_target_fixation'");

    附一个我写的C#上使用EyeLink的框架(MIT LICENSE):http://files.cnblogs.com/mayswind/DotMaysWind.EyeLink.rar

    【相关链接】

    1、SR Research Support Site:https://www.sr-support.com/forums/


    如果您觉得本文对您有所帮助,不妨点击下方的“推荐”按钮来支持我!

    本文及文章中代码均基于“署名-非商业性使用 3.0”,文章欢迎转载,但请您务必注明文章的作者和出处链接,如有疑问请私信我联系!

    我在参加51CTO的博客比赛,如果您觉得我还不错,请投我一票,谢谢!http://blog.51cto.com/contest2013/1788535

     
    分类: C#

    用c#实现单链表(程序代码已经验证,完全正确)

    1.程序的大致结构如下图:

    2.下面依次列出各个类的代码

    ①ILISTDs.cs  这是一个接口类,列出单链表的方法

    复制代码
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    
    namespace 单链表
    {
       public interface IListDs<T>
        {
           int GetLength();//求长度
           void Clear();//清空操作
           bool IsEmpty();//判断线性表是否为空
           void Append(T item);//附加操作
           void Insert(T item,int i);//插入操作
           T Delete(int i);//删除操作
           T GetElem(int i);//取表元
           int Locate(T value);//按值查找
        }
    }
    复制代码

    ②LinkList.cs 单链表的实现类

    复制代码
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    
    namespace 单链表
    {
        public class LinkList<T> : IListDs<T>
        {
            private Node<T> head;//单链表的头引用
            //头引用的属性
            public Node<T> Head
            {
                get
                {
                    return head;
                }
                set
                {
                    head = value;
                }
            }
            //构造器
            public LinkList()
            {
                head = null;
            }
            //求单链表的长度
            public int GetLength()
            {
                Node<T> p = head;
                int len = 0;
                while (p != null)
                {
                    p = p.Next;
                    len++;
                }
                return len;
            }
            //清空单链表
            public void Clear()
            {
                head = null;
            }
            //判断是否为空
            public bool IsEmpty()
            {
                if (head == null)
                {
                    return true;
                }
                else
                {
                    return false;
                }
            }
            //在单链表的末尾添加新元素
            public void Append(T item)
            {
                Node<T> q = new Node<T>(item);
                Node<T> p = new Node<T>();
                if (head == null)
                {
                    head = q;
                    return;
                }
                p = head;
                while (p.Next != null)
                {
                    p = p.Next;
                }
                p.Next = q;
            }
            //在单链表第i个位置前面插入一个值为item的节点
            public void Insert(T item, int i)
            {
                if (IsEmpty() || i < 1)
                {
                    Console.WriteLine("链表为空或者位置错误");
                    return;
                }
                if (i == 1)
                {
                    Node<T> q = new Node<T>(item);
                    q.Next = head;
                    head = q;
                    return;
                }
                Node<T> p = head;
                Node<T> r = new Node<T>();
                int j = 1;
                while (p.Next != null && j < i)
                {
                    r = p;
                    p = p.Next;
                    j++;
                }
                if (j == i)
                {
                    Node<T> q = new Node<T>(item);
                    Node<T> m = r.Next;
                    r.Next = q;
                    q.Next = m;
                }
            }
            //在单链表第i个位置后面插入一个值为item的节点
            public void InsertPost(T item, int i)
            {
                if (IsEmpty() || i < 1)
                {
                    Console.WriteLine("链表为空或者位置错误");
                    return;
                }
                if (i == 1)
                {
                    Node<T> q = new Node<T>(item);
                    q.Next = head.Next;
                    head.Next = q;
                    return;
                }
                Node<T> p = head;
                Node<T> r = new Node<T>();
                int j = 1;
                while (p.Next != null && j <= i)
                {
                    r = p;
                    p = p.Next;
                    j++;
                }
                if (j == i+1)
                {
                    Node<T> q = new Node<T>(item);
                    Node<T> m = r.Next;
                    r.Next = q;
                    q.Next = m;
                }
                else
                {
                    Console.WriteLine("插入位置过大,error");
                }
            }
            public T Delete(int i)
            {
                if (IsEmpty() || i < 1)
                {
                    Console.WriteLine("链表为空或者位置错误");
                    return default(T);
                }
                Node<T> q = new Node<T>();
                if (i == 1)
                {
                    q = head;
                    head = head.Next;
                    return q.Data;
                }
                Node<T> p = head;
                int j = 1;
                while (p.Next != null && j < i)
                {
                    q = p;
                    p = p.Next;
                    j++;
                }
                if (j == i)
                {
                    q.Next = p.Next;
                    return p.Data;
                }
                else
                {
                    Console.WriteLine("位置不正确");
                    return default(T);
                }
            }
            //获得单链表第i个元素
            public T GetElem(int i)
            {
                if (IsEmpty())
                {
                    Console.WriteLine("链表是空链表");  
                    return default(T);
                }
                Node<T> p = new Node<T>();
                p = head;
                int j=1;
                while(p.Next!=null&&j<i)
                {
                    p = p.Next;
                    j++;
                }
                if (j == i)
                {
                    return p.Data;
                }
                else
                {
                    Console.WriteLine("位置不正确!");
                }
                return default(T);
    
            }
            //在单链表中查找值为value的节点
            public int Locate(T value)
            {
                if (IsEmpty())
                {
                    Console.WriteLine("链表是空链表!");
                    return -1;
                }
                Node<T> p = new Node<T>();
                p = head;
                int i = 1;
                while (((p.Next!=null)&&(!p.Data.Equals(value))))
                {
                    p = p.Next;
                    i++;
                }
                if (p == null)
                {
                    Console.WriteLine("不存在这样的节点。");
                    return -1;
                }
                else
                {
                    return i;
                }
            }
        }
    }
    复制代码

    ③ Node.cs   节点类

    复制代码
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    
    namespace 单链表
    {
        public class Node<T>
        {
            private T data;//数据域
            private Node<T> next;//引用域
            //构造器
            public Node(T val, Node<T> p)
            {
                data = val;
                next = p;
            }
            //构造器
            public Node(Node<T> p)
            {
                next = p;
            }
            //构造器
            public Node(T val)
            {
                data = val;
            }
            //构造器
            public Node()
            {
                data = default(T);
                next = null;
            }
            //数据域属性
            public T Data {
                get {
                    return data;
                }
                set {
                    data = value;
                }
            }
            //引用域属性
            public Node<T> Next {
                get {
                    return next;
                }
                set {
                    next = value;
                }
            }
        }
    }
    复制代码

    ④Program.cs     主程序

    复制代码
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    
    namespace 单链表
    {
        class Program
        {
            static void Main(string[] args)
            {
                LinkList<string> link = new LinkList<string>();
                link.Append("123");
                link.Append("567");
                link.Append("jqk");
                link.Insert("abc",2);
                link.InsertPost("def",2);
                int length = link.GetLength();
                int k=link.Locate("567");
                string m=link.GetElem(3);
                Console.WriteLine("567的位置为"+k);
                Console.WriteLine("位置为3的值为"+m);
                Console.WriteLine("链表的长度为"+length);
                Node<string> n = link.Head;
                while (n != null)
                {
                    Console.WriteLine(n.Data);
                    n = n.Next;
                }
            }
        }
    }
    复制代码

    ⑤运行结果如下图,和预测结果完全一致

  • 相关阅读:
    Civil 3D 二次开发 创建Civil 3D 对象—— 01 —— 创建几何空间点
    Civil 3D 二次开发 创建Civil 3D 对象—— 00 ——
    Civil 3D 二次开发 创建AutoCAD对象—— 01 —— 创建直线
    Civil 3D 二次开发 新建CLR项目出现错误C2143
    Civil 3D 二次开发 创建AutoCAD对象—— 00 ——
    了解AutoCAD对象层次结构 —— 6 ——块表记录
    datepicker97使用
    使用angular 外接 templateUrl,使用ng-include
    angularJs 遮罩
    网上找的有关css兼容问题
  • 原文地址:https://www.cnblogs.com/Leo_wl/p/3425804.html
Copyright © 2011-2022 走看看