Files
IOT_SIM/docs/教材摘要/1-项目1到项目3前半.md
xuyueandClaude Opus 5.5 98be8331bc 第 0 阶段:教材项目 2 任务 1 在网页中原样运行
- runtime:仿真 WinForms 子集(Form、Button、Label、TextBox、ComboBox、
  GroupBox、Timer、MessageBox、界面线程消息循环、跨线程检查)、
  教材设备类库(DeviceCtrlHelper、LED 控件)、运行器(常驻编译服务 + 运行模式)
- server:Bun + Hono,WebSocket 会话,每次运行一个进程,消息转发
- web:Vue + Naive UI,WinForms 风格窗体渲染、MessageBox、实训台 LED 屏
- 端到端测试与 100 个程序并发的压力测试
- 教材逐任务摘要(docs/教材摘要)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 10:15:14 +08:00

60 KiB
Raw Permalink Blame History

《物联网编程与应用 第2版》摘要 · 第1部分(PDF 第1–65页)

页码换算:书页 = PDF页 − 3(PDF 4 = 书 p1)。扫描件里没有前言和二维码索引,只有目录。 本部分结束于 PDF 65 = 书 p62,即项目3 任务2 的【知识提炼】中间(RadioButton“设置字体颜色”示例,表3-5 之后)。下一位读者从 PDF 66 / 书 p63 接着读(应为该示例的代码、CheckBox 知识点、能力拓展,以及书 p71 的项目3评价)。 参考工程目录:/home/xuyue/Projects/IOT/物联网编程与应用配套/物联网编程与应用/项目N/任务一|二|三(文件夹用中文数字)。

0. 目录(PDF 1–3)

项目 / 任务 书页 PDF页
项目1 C#入门基础 1 4
 任务1 C#引入 1 4
 任务2 C#面向对象特性 13 16
 项目评价 21 24
项目2 智慧校园系统——环境监测模块 23 26
 任务1 LED显示屏的使用 23 26
 任务2 实时采集温度、湿度和光照度数据 36 39
 任务3 在LED显示屏中显示环境数据 42 45
 项目评价 51 54
项目3 智慧校园系统——智慧路灯控制模块 53 56
 任务1 使用ZigBee技术控制路灯开关 53 56
 任务2 实现人体红外传感器和路灯联动 58 61
 项目评价 71 74
项目4 入侵信息监测模块:任务1 采集红外对射入侵传感器数据 73;任务2 控制网络摄像机 78;任务3 控制报警灯 84;评价 89 73 76
项目5 灾情信息监测模块:任务1 采集火焰传感器数据 91;任务2 采集烟雾传感器数据 98;任务3 实时推送灾情信息 104;评价 110 91 94
项目6 数据存储模块:任务1 制作环境数据表 113;任务2 存储校园环境数据 119;任务3 查询校园安防数据 127;评价 131 113 116
项目7 情景控制模块:任务1 实现智慧教室 133;任务2 实现智慧寝室 141;任务3 实现智慧会议室 148;评价 156 133 136
项目8 智慧校园管理平台开发:任务1 制作管理平台的主界面 157;任务2 制作“校园环境”界面 159;任务3 实现“校园环境”数据采集及数据分析功能 164;任务4 实现“校园安防”功能模块 174;任务5 实现“校园安防”模块中显示历史图片的功能 181;评价 185 157 160
参考文献 187 190(超出187页的扫描件,可能缺页)

(注意:扫描件只有187页,所以书页 ≥ 185 的内容可能不在 PDF 里。)


项目1 C#入门基础(书 p1–22 / PDF 4–25)

项目概述:C# 是微软在 C 和 C++ 基础上衍生出的面向对象语言,基于 .NET。本项目先学 Python 示例程序,再逐步引入 C#,为后面的智慧校园开发做准备。

学习目标

  • 了解 C# 与 Python 的语法区别。
  • 了解 C# 的语法规范。
  • 理解属性和 this 关键字。
  • 养成良好的代码编写规范。
  • 增强职业认识,提升职业素养,树立职业信心。

任务1 C#引入(书 p1–13 / PDF 4–16)

任务描述:分别用 Python 和 C# 写一个登录程序,通过对比了解 C# 的命名空间、属性、this 等面向对象概念。

任务实施

1. Python 登录应用体验(书 p2):控制台程序“登录”,用 usernameLst = list() 和 pwdLst = list() 保存用户。

  • def ligon():用 input 读入用户名和密码,if username in usernameLst and pwd in pwdLst 时打印“登录成功”,否则打印“登录失败”。
  • def addUser():读入新用户名并 append,读入新密码并 append,打印“添加用户成功!”。
  • while(True):提示“请输入您要操作的内容(1.登录;2.新增用户,输入q退出程序)”;输入 '1' 调 ligon(),'2' 调 addUser(),'q' 执行 break。
  • 参考代码见 项目1/任务一/python/登录.py,内容与书一致(参考代码里少了 q 分支)。模拟器如果要支持这一步,需要一个 Python 控制台(stdin/stdout)。

2. C# 登录应用体验(书 p2–7)

  1. 新建 Windows 窗体应用程序,名称“登录”。
  2. 把 Form1 改名为 Login,用工具箱搭出“登录”界面(图1-3:两个标签、两个文本框,按钮“登录 / 取消 / 增加用户”)。
  3. 在“解决方案资源管理器”里选“添加 → 类”,新建 PublicData:
    public class PublicData {
      public static string[] SysUserName = new string[4];//存储系统用户名
      public static string[] SysPwd = new string[4];//存储系统用户密码
      public static int intNum { get; set; }//当前数据存储的用户数量
      public static int intUser { get; set; }//记录当前用户索引号
    }
    
  4. 在 Login 中加全局变量 AddUser addfrm = new AddUser(); 和 public bool IsLogin = false;。
  5. 写 btnOk_Click:遍历 PublicData.SysUserName;用户名和密码都匹配且不为空时,令 IsLogin=true,弹 MessageBox.Show("登录成功,欢迎进入!","登录"),并记下 intUser = i。书里接着是 if (change) { this.Hide(); change = false; },然后 break。都不匹配时提示“无效的密码或用户名,请重试”,并执行 txtPassword.Focus()。
  6. 写 btnCancel_Click:IsLogin=false; this.Close();
  7. 写 btnAdd_Click:addfrm.Show();
  8. 添加 Windows 窗体 addfrm(图1-6“增加用户”界面)。
  9. 写 addfrm 的 btnOk_Click:当 intNum<4 且 txtPass1.Text.Equals(txtPass2.Text) && txtPass2.Text != "" 时,把 txtNewUser1.Text 和 txtPass1.Text 存进数组,intNum++,再 this.Close()。否则提示“用户名或密码错误,请重新输入”。数组满了提示“用户名已满不能增加”(标题都是“新增用户”)。
  10. 写 addfrm 的 btnCancel_Click:this.Close();

表1-1 “登录”界面控件的属性

控件名称 控件类型 属性名 属性值 控件功能
Login 窗体 Text 登录
LblUsername 标签 Text 用户名称
LblPwd 标签 Text 密码
txtUserName 文本框 输入用户名
txtPwd 文本框 PasswordChar * 输入密码
BtnOk 按钮 Text 登录
BtnCancel 按钮 Text 取消
BtnAdd 按钮 Text 增加用户

表1-2 “增加用户”界面控件的属性

控件名称 控件类型 属性名 属性值 控件功能
addfrm 窗体 Text 增加用户
LblUsername 标签 Text 用户名
LblPwd 标签 Text 密码
LblCheckPwd 标签 Text 确认密码
txtUserName 文本框 输入用户名
txtPwd 文本框 PasswordChar * 输入密码
txtCheckPwd 文本框 PasswordChar * 确认密码
BtnOk 按钮 Text 确定
BtnCancel 按钮 Text 取消

小贴士(关于 VS IDE,模拟器需要有对应的界面)

  • “工具箱”面板在 VS 左侧,按栏目列出控件。双击控件就能加到窗体上,也可以拖上去。
  • “属性”面板在 VS 右下方,用来设置控件属性,也用来管理控件事件。

知识提炼(书 p7–12)

  1. 类:类是创建对象的模板。用 类修饰符 class 类名 { } 声明,例如 public class People { string name; int age; }。静态类只能通过类名访问。继承写成 class classChild : newClassName。小贴士:继承时子类和基类之间必须用冒号。
  2. 对象:用 new 类名() 创建。示例 Student 类有字段 name、无参构造函数(name="unknown")、带参构造函数和 SetName 方法;最后用 MessageBox.Show(student1.name + " " + student2.name) 输出。
  3. 命名空间:用 using 命名空间名称; 引入。示例 namespace TestN { class A { PrintMsg() → MessageBox.Show("物联网编程与应用") } },在 Form1_Load 中 new A().PrintMsg()。
  4. 注释:单行用 //,多行用 /* */。示例是控制台程序,用到 Console.WriteLine 和 Console.ReadLine。
  5. 属性:用 get/set 控制对私有字段的读写,至少要有其中一个。示例 Student 类:School 只读(默认值 "浙江**职业中学"),Name 只写,Age 可读写;Display() 用 Console.WriteLine 输出;Main 中用 Console.Write("学校:"+stu1.School)。
  6. this 关键字:this 代表当前实例,静态方法中不能用。示例 this.Age = age; this.Name = name;。

能力拓展

边想边做(书 p12–13):制作登录界面,登录成功后跳转,用属性读写,并在界面上显示当前登录的用户名。

提示:根据前面学的知识补全下面的代码,再输入完整代码并调试。

public class Student
{
    private string name="admin";//设置默认用户
    private string pwd ="admin";//设置默认密码
    // 声明Name属性,可进行读写
    public string Name
    {
        set
        {
            name = value;
        }
        ____ //设置get属性
        {
            ____ //返回name值
        }
    }
    ____ // 声明pwd属性,可进行读写
    {
        ____ //设置set属性
        {
            ____ //赋值pwd值
        }
        get
        {
            return pwd;
        }
    }
}

private void BtnLogin_Click(object sender, EventArgs e)
{
    if (txtUsername.Text != "" &&txtPwd.Text != "")//判断输入内容是否为空
    {
        if (txtUsername.Text == stu.Name&&txtPwd.Text == stu.Pwd)
        {
            ____ //实例化main窗体
            ____ //将当前对象的stu对象赋值给main窗体对象的stu
            ____ //使用Show方法显示main窗体
            ____ //使用Hide方法将当前窗体对象隐藏
        }
        else
        {
            MessageBox.Show("登录失败,用户名或密码错误!", "登录");
        }
    }
    else
    {
        MessageBox.Show("用户名或密码为空!", "登录");
    }
}

(参考答案大致为:get / return name; / public string Pwd / set / pwd = value; / Main main = new Main(); / main.stu = stu; / main.Show(); / this.Hide();。这里隐含要求:Login 窗体有字段 Student stu = new Student();,另有一个 Main 窗体,它有 public 字段 stu,并在界面上显示 stu.Name。书里没有给 Main 窗体的界面图或控件表。)

自主探究(书 p13):把模块改成多用户登录系统,系统里有 10 个用户,要求:

  1. 10 个用户能分别登录和退出。
  2. 用对象存储用户数据,并用属性控制字段的读写。

任务2 C#面向对象特性(书 p13–21 / PDF 16–24)

任务描述:面向对象有封装、继承、多态三个特性。完成本任务后,学生能初步理解 C# 的装箱与拆箱、多态性,以及 List、Dictionary 的用法。

任务实施

  1. 新建 Windows 窗体应用程序“C#基础”(存放在 项目1\任务2)。
  2. 按图1-7 搭界面:groupBox1“简易计算器”里放 txtNum1、运算符下拉框、txtNum2、“=”按钮、结果框;groupBox2“类的多态性”里放一个多行文本框和“显示”按钮。
  3. 写 public Object Result(Object a, Object b, String type):先拆箱成 float,再 switch 处理 + - * /,default 返回 0,最后 return (Object)result;(装箱)。
  4. “=”按钮 btnCalculate_Click:两个文本框都不为空时,float.Parse 两个数,float res = (float)Result(a, b, cmboxType.Text),再令 txtResult.Text = res.ToString()。
  5. 新建 People 类:public virtual void Speak(TextBox Txtbox, string name) { Txtbox.Text += name; }
  6. 新建 Chinese : People,override 后执行 base.Speak(Txtbox, name+"说的是汉语!\r\n")。
  7. 新建 American : People,override 后执行 base.Speak(Txtbox, name+"说的是英语!\r\n")。
  8. 写“显示”按钮 btnDisplay_Click(书上第8步误写成“双击‘=’按钮”):先建 Dictionary<int,string>,内容为 {1:"张三", 2:"李四", 3:"Mary", 4:"Jimmy"};再建 List<People>,放 2 个 Chinese 和 2 个 American;最后遍历执行 peoplelist[i].Speak(this.txtDispaly, dicName[i+1])。
  9. 运行:例如输入 1、+、2 后得 3;点“显示”后文本框输出“张三说的是汉语!/李四说的是汉语!/Mary说的是英语!/Jimmy说的是英语!”(图1-8)。

表1-3 “C#入门基础”界面控件的属性

控件名称 控件类型 属性名 属性值 控件功能
Form1 窗体 Text C#入门基础
groupBox1 组合框 Text 简易计算器
txtNum1 文本框 输入数字1
txtNum2 文本框 输入数字2
cmboxType 下拉列表框 选择运算符
btnCalculate 按钮 Text = 计算结果
txtResult 文本框 ReadOnly True 显示计算结果
groupBox2 组合框 Text 类的多态性
txtDispaly 文本框
btnDisplay 按钮 Text 显示

(书中没写,但模板必须有:cmboxType.Items = "+","-","*","/",txtDispaly.Multiline = true。参考工程里就是这样设置的。)

知识提炼(书 p16–20,示例大多是控制台程序,用 Console.WriteLine / Console.Read)

  1. 装箱和拆箱:装箱把值类型转成引用类型(Object obj = str;),拆箱把引用类型转回值类型(string s = (string)obj;)。示例里用 Console.WriteLine("...{0}...{1}", str, obj) 做格式化输出。
  2. 多态:
    • 重载(overload):示例 ClaArea.Area(double r) 返回 Math.PI*r*r,Area(double x, double y) 返回 x*y。Area(4) 输出 50.26482,Area(3,4) 输出 12。
    • 虚方法(virtual)与重写(override):MyVirtualFun.Fun() 输出“我是父类虚方法”,OverrideFunction : MyVirtualFun 重写后输出“我是子类方法”。
  3. List<T>:表1-4 常见属性有 Count 和 Capacity(Capacity ≥ Count)。表1-5 常见方法有 Add、AddRange、Clear、Contains、CopyTo(T)、Find、IndexOf(T)、Insert、InsertRange、LastIndexOf(T)、Remove、Reverse()、Sort()、TrimExcess。示例依次调用:Add("A")、AddRange({"B","C","D"})、Capacity、Count、Contains("D")、Insert(2,"E")、CopyTo(array[10])、Remove("B")、Sort()、TrimExcess()、Clear()。
  4. Dictionary 类:按键查找值,键必须唯一。格式 Dictionary<key,value> DemoDic = new Dictionary<key,value>();。示例用到 Add、按索引器取值 Dic[2]、ContainsKey(3)、Remove(2)、ContainsValue("张三")。

能力拓展

边想边做(书 p20–21):“任务实施”中已经用过虚方法和 List。这里要定义形状父类,并用圆类实现其虚方法。按图1-9 创建界面:窗体标题“面积计算”;标签“请输入圆的半径:”加文本框 txtCircleR 和“添加”按钮;“显示面积”按钮;下方一个大的多行文本框 txtArea。按下面的提示实现功能。

  1. 定义一个 Shape 类:
class Shape
{
    ____ //定义虚方法Area,无返回值,参数为TextBox类型
    {
    }
}
  1. 定义一个 Circle 类,继承 Shape:
class Circle: Shape
{
    double area;
    public Circle(double R)
    {
        ____ //实现构造函数,将R变量赋值给当前变量r
    }
    ____ //重写父类Area方法
    {
        area= Math.PI____; //实现面积计算
        txtArea.Text =txtArea.Text+ area.ToString()+"\r\n";
    }
}
  1. 在“添加”按钮的 Click 事件里按提示补全代码:
private void btnAdd_Click(object sender, EventArgs e)
{
    Shape CirTemp;
    if (txtCircleR.Text != "")
    {
        ____; //实例化Circle类
        ____; //将Shape对象加入list列表
        txtCircleR.Text = "";
    }
    else
    {
        MessageBox.Show("输入半径大小");
    }
}

(书中代码有缺陷:Circle 没有声明字段 r,需要补 double r;。Area 方法的参数名应为 txtArea。“显示面积”按钮的代码书里没给,应为遍历 list 并调用 Area(txtArea)。窗体还需要字段 List<Shape> list = new List<Shape>();。)

自主探究(书 p21):定义形状类,在其中封装 X 和 Y 两个变量,并定义一个虚方法;再定义一个继承形状类的矩形类,实现该虚方法,输出 X×Y 的值。用 List 存 5 个矩形对象,循环输出矩形面积。

项目1 项目评价(书 p21–22 / PDF 24–25)

1. 项目评价指标(表中没有分值)

任务 评价指标 具体要求
任务1 类与对象的概念 学会创建自定义类、实例化对象,理解类与对象
任务1 窗体界面 窗体控件正确,布局美观
任务1 单击事件 按钮单击事件的正确使用
任务1 属性、this 理解属性、this关键字
任务2 拆箱与装箱 能正确使用拆箱与装箱
任务2 多态性 重载、虚函数、重写的正确使用
任务2 List 了解List的使用
任务2 Dictionary 了解Dictionary的使用
职业素养 代码书写规范 应该遵从结构化的要求,采用缩进的格式
职业素养 注释书写规范 做到清晰、准确地描述内容。对于程序中复杂的部分必须有注释加以说明。注释量要适中,避免过多或过少导致阅读困难
职业素养 变量命名规范 选择有意义的英文进行命名,使变量名能清楚地表达其含义。尽量少使用人名、地名和汉语拼音。应使用那些约定俗成的缩写。多个单词组成的变量名,每个单词的首字母应该大写

2. 个人学习总结:只有两块空白,“项目学习情况:”和“心得与反思:”,供学生填写。

项目1:书与参考工程的差异,以及模拟器要注意的点

  • 任务1 控件命名不一致。参考工程 项目1/任务一/C#/frmlogin:
    • 密码框叫 txtPassword(表1-1 写 txtPwd,但书上代码用的是 txtPassword)。
    • 标签是 label1/label2,Text 为“用户名称(&U)”和“密码(&P)”(带助记符 &)。
    • btnOk 的 Text 是“确定”(书上是“登录”)。
    • Login 的 Load 事件 frmExam5_8_Load 预置了用户 admin/admin,书中没有这一步。没有它,第一次运行时没有可登录的用户。
    • “增加用户”窗体类名是 AddUser(Name 为 "frmExam_5_82"),书上叫 addfrm。文本框是 txtNewUser1/txtPass1/txtPass2,与表1-2 的 txtUserName/txtPwd/txtCheckPwd 不一致,但和书上代码一致。模板应以代码中用到的名字为准。
  • 书中 btnOk_Click 用了未声明的变量 change,属于书的错误。参考工程没有这个变量,另有一个 btnChange_Click,它先 btnOk.PerformClick(),登录成功再 this.Hide()。但设计器里没有 btnChange 按钮,这段是死代码。
  • 任务1 需要多窗体支持:new AddUser()、Show()、Hide()、Close();拓展练习还要跨窗体传对象(main.stu)。也要支持 static 类成员和自动属性。
  • 任务2 参考工程中按钮叫 BtnCalculate(B 大写),handler 是 BtnCalculate_Click;书上是 btnCalculate。窗体 Text 为“c#入门基础”。
  • 知识提炼的示例全是控制台程序(Console.WriteLine/Write/ReadLine/Read 和 {0} 格式化)。平台如果想让学生运行这些示例,需要一个控制台输出面板。
  • 任务1 第一部分是 Python 控制台程序。
  • 书中把运算符 "-" 排成了 "–"(en dash),抄写时要改回 ASCII 减号。

项目2 智慧校园系统——环境监测模块(书 p23–52 / PDF 26–55)

项目概述:LED 显示屏是校园的信息发布平台。本项目用温湿度传感器和光敏电阻传感器采集数据,并显示在校园 LED 显示屏上。

学习目标

  • 掌握串口通信的基本概念和工作原理。
  • 了解 LED 显示屏的工作原理及数据推送方法。
  • 掌握传感器的工作原理及数据采集方法。
  • 熟悉温湿度传感器、光敏电阻传感器的工作原理。
  • 理解模拟量采集器 ADAM4017 的功能及使用方法。
  • 遵循代码编写规范,养成正确的变量命名与注释习惯,培养思维逻辑能力。
  • 增强职业认识,提升职业素养,树立职业信心。

任务1 LED显示屏的使用(书 p23–36 / PDF 26–39)

任务描述:模拟智慧校园信息发布系统,用 C# 写程序通过串口把信息发送到 LED 显示屏。学生要学会向 LED 显示屏发送信息、使用 groupBox 控件,并了解 C# 的错误处理方法。

任务实施

  1. 把 LED 显示屏通过串口接到 PC(没有硬件的读者跳过)。
  2. 新建 Windows 窗体应用程序“LED显示屏”(项目2\任务1)。
  3. 从配套资源中把 DeviceCtrlLibrary.dll 和 “API” 目录下的全部 dll 复制到 bin\Debug。API 目录有 ComLibrary.dll、ICS.Acquisition.dll、ICS.Common.dll、ICS.Models.dll、IPCameraDll.dll、LEDLibrary.dll、MyCamer.dll。
  4. 添加引用 DeviceCtrlLibrary.dll。没有硬件的读者添加仿真动态库文件,不用添加 API 文件夹里的 dll。
  5. 把自定义控件加入工具箱:右击工具箱“常规”,选“选择项”,点“浏览”,加载 WinFormsDeviceControlLibrary.dll。加载后“常规”下出现 4 个控件:Alarm、Lamp、LED、ShowMsg(图2-5)。
  6. 按图2-6 搭界面:上方是大块 LED 控件;下方 groupBox“LED控制”中有“串口”下拉框、“打开”按钮、“推送LED信息”文本框和“推送”按钮。
  7. 添加 using:System.IO.Ports、DeviceCtrlLibrary,以及 WinFormsDeviceControlLibrary(注释写“引用外部窗体类所在的命名空间”)。
  8. btn_Open_Click:DeviceCtrlHelper.ledHelper.OpenLedCom(com_Port.Text);
  9. btn_Send_Click:string res = DeviceCtrlHelper.ledHelper.SendLedString(txt_SendText.Text); 如果 res.IndexOf("Error") >= 0,执行 MessageBox.Show(res),否则执行 led.setLedText(res)。
  10. com_Port_Click:先 com_Port.Items.Clear(),再 foreach (string item in SerialPort.GetPortNames()) com_Port.Items.Add(item);
  11. 运行:选串口(图中是 COM2),输入“欢迎学习物联网”,先点“打开”再点“推送”。仿真环境下 LED 控件会大字显示这段文字(图2-7)。

表2-1 “LED显示屏”窗体中控件的属性

控件名称 控件类型 属性名 属性值 控件功能
Form1 窗体 Text LED显示屏
LED 自定义控件 显示LED推送的信息
groupBox1 组合框 Text LED控制 将功能控件放在同一个框里
Label1 标签 Text 串口
Label2 标签 Text 推送LED信息
btn_Open 按钮 Text 打开 打开(关闭)串口
btn_Send 按钮 Text 推送 向LED显示屏发送信息
com_Port 下拉列表框 显示串口号
txt_SendText 文本框 输入要发送的信息

小贴士(仿真相关)

  • 书 p27 原文:“本项目使用的是硬件测试功能,若要使用仿真功能,请将资源文件下的 DeviceCtrlLibrary.dll 文件复制到 D:\物联网编程与应用\项目2\任务1\LED显示屏\LED显示屏\bin\Debug 文件夹下。”也就是说,仿真版和硬件版用的是同名的 DeviceCtrlLibrary.dll,API 完全相同,只是换一个 dll。
  • 结果判断约定:SendLedString 失败时返回带 "Error" 的字符串,代码用 IndexOf("Error") 判断。

知识提炼(书 p28–34)

  1. 串口通信及串口波特率:串口即 Serial Port / COM 接口,按位顺序传输。接口标准有 RS-232C、RS-422、RS-423、RS-485,涉及 DTE 和 DCE。波特率例子:9600bit/s 约等于每秒 1200 字节。两端波特率必须一致,否则会出现乱码。
  2. SerialPort 类:
    • 表2-2 常见属性:BaseStream、BaudRate、BytesToRead、BytesToWrite、CDHolding、CtsHolding、DataBits、Encoding、ParityReplace、ReadBufferSize、ReadTimeout、ReceivedBytesThreshold、StopBits、WriteBufferSize、WriteTimeout、PortName。
    • 表2-3 常见方法:Close、Open、Read、ReadByte、ReadChar、ReadLine、ReadTo、Write、WriteLine。
    • 发送示例:new SerialPort(),PortName="COM1",BaudRate=9600,Open,用 Encoding.Unicode.GetBytes("ABC") 转字节再转 Base64,WriteLine 后 Close。接收示例:ReadLine 后 Convert.FromBase64String,再 Encoding.Unicode.GetString,最后 MessageBox 显示。
  3. C# 基本数据类型(表2-4):decimal、bool、char、byte、sbyte、short、int、long、ushort、uint、ulong、float、double、string,表中列出了各类型的位数和范围。
  4. 变量:区分大小写;以字母开头;只能由字母、数字、下划线和 @ 组成;不能与关键字或库函数同名(书中举 char use; 不合法、char @use; 合法,其实 use 不是 C# 关键字,这是书的错误)。作用域分字段、局部变量、循环变量。示例:全局 int i=5,BtnShow_Click 中弹出全局 i,再调 showi() 弹出局部 i=1。
  5. 运算符:
    • 表2-5 算术:+ - * / % ++ --。示例 a=8, b=3:a*b=24,a/b=2,a%b=2。
    • 表2-6 比较:== != > < >= <=。
    • 表2-7 逻辑:&& || !。
    • 表2-8 赋值:= += -= *= /= %=。
  6. 错误处理 try-catch:除以 0 时程序会弹出“未经处理的异常…尝试除以零”的对话框(图2-8),加 try-catch 后改为 MessageBox.Show("程序出现不可预知的错误:"+ex.Message)。(书中误写为 Catch(EventArgs ex),应为 catch (Exception ex)。)
  7. 动态链接库文件:DeviceCtrlLibrary.dll 和 WinFormsDeviceControlLibrary.dll 是编者提供的 dll,功能见“设备控制类库使用说明.doc”,使用前必须先加载、引用。

能力拓展

边想边做(书 p34–36):做一个在 LED 上显示计算结果的小应用。参考界面图2-9,窗体标题“LED显示计算结果”:

  • 上方是 LED 控件。
  • 中部 groupBox“简单计算器”:[textBox1] + [textBox2] = [textBox3] 和“计 算”按钮。
  • 下部 groupBox“打开串口”:“串口”下拉框和“打开串口”按钮。

提示1:补全代码后,双击“计算”按钮,把代码输入代码编写窗口。

private void btn_Calculation_Click(object sender, EventArgs e)
{
    int add_1 = 0;            //定义变量
    int ____;                 //定义变量
    int ____;                 //定义变量
    int ____;                 //定义变量
    string right = DeviceCtrlHelper.ledHelper.SendLedString("你答对了!");
    string erro = DeviceCtrlHelper.ledHelper.SendLedString("你答错了!");
    if (IsOpen)
    {
        if (textBox1.Text != "" && textBox2.Text != "" && textBox2.Text != "")
        {
            try
            {
                //将textBox1中的文本转换成数值
                add_1 = int.Parse(textBox1.Text);
                add_2 = ____;
                add_3 = ____;
                counter = ____;//求和
                if (counter == add_3)
                {
                    if (right.IndexOf("Error") >= 0)
                    {
                        MessageBox.Show(right);    //出错提示
                    }
                    else
                    {
                        led.setLedText(right); //LED显示屏输出信息
                        MessageBox.Show("你答对了!");
                    }
                }
                else
                {
                    if (erro.IndexOf("Error") >= 0)
                    {
                        ____;    //出错提示
                    }
                    else
                    {
                        ____;    //LED显示屏输出信息
                        MessageBox.Show("你答错了!");
                    }
                }
            }
            catch (Exception)
            {
                //输入格式出错提醒
                MessageBox.Show("请正确的格式。", "提醒");
            }
        }
        else
        {
            //没有输入提醒
            MessageBox.Show("请你输入!,", "提醒");
        }
    }
    else
    {
        //没有设置串口提醒
        MessageBox.Show("请设置好串口", "警告");
    }
}

(几点说明:第三个判断原文就是重复的 textBox2.Text != "",应为 textBox3;变量名 add_2、add_3、counter 由后文推出;IsOpen 需要“打开串口”按钮来设置,书中没给这段代码;right/erro 在判断 IsOpen 之前就调用了 SendLedString,串口未打开时仿真库可能返回 Error,也可能抛异常,模拟器需要定义这种行为。)

提示2:btn_Calculation 单击事件流程图(图2-10)的文字描述:

  • 开始,初始化变量,判断 IsOpen 是否为 true。
    • 否:提示信息“串口参数没有设置好”,结束。
    • 是:判断三个文本框是否都不为空。
      • 否:提示信息“请你输入信息”,结束。
      • 是:把三个文本框的文本转成数值,求和,再判断求和结果与输入的数据是否相等。
        • 正确:判断串口通信是否正常。否则提示“串口出错信息”;正常则向 LED 屏输出正确信息,并弹出程序提示“你答对了”。
        • 不正确:判断串口通信是否正常。否则提示“串口出错信息”;正常则向 LED 屏输出答错信息,并弹出程序提示“你答错了”。
  • 所有分支都到“结束”。

自主探究(书 p36):给该模块增加乘法和除法计算功能。

任务2 实时采集温度、湿度和光照度数据(书 p36–42 / PDF 39–45)

任务描述:用 C# 写程序,把传感器采集的数据显示在计算机上。学生要学会使用 Label 标签和 Button 按钮。(有“扫码看视频”。)

任务实施

  1. 把模拟量采集器 ADAM4017 接到 PC 串口(没有的跳过)。
  2. 新建 Windows 窗体应用程序“模拟量采集”(项目2\任务2)。
  3. 把 DeviceCtrlLibrary.dll 和 API 目录下的全部 dll 复制到 bin\Debug。
  4. 添加引用 DeviceCtrlLibrary.dll(没有硬件的读者添加仿真 dll,不加 API)。
  5. 按图2-11 搭界面:左边 groupBox“数据显示”里有光照度、温度、湿度三组“标签+文本框”,右侧一个竖长的“获取”按钮;下方 groupBox“配置”里有“串口”下拉框和“开启”按钮。
  6. 添加 using:System.IO.Ports、DeviceCtrlLibrary、DeviceCtrlLibrary.Model。
  7. 定义全局变量 bool IsOpen=false; 和 ADAM4017ResModel ResModel;。
  8. frm_analog_Load(窗体 Load 事件):清空 com_port,把 SerialPort.GetPortNames() 的结果全部加进去。
  9. btn_Open_Click:如果 !IsOpen,执行 DeviceCtrlHelper.adam4017Helper.Open(com_port.Text),按钮文字改为“关闭”,IsOpen=true;否则执行 adam4017Helper.Close(),按钮文字改为“开启”,IsOpen=false。
  10. btn_get_Click:ResModel = DeviceCtrlHelper.adam4017Helper.SetData();,然后令 txt_light.Text = ResModel.lightValue、txt_humidity.Text = ResModel.humidityValue、txt_temperature.Text = ResModel.temperatureValue。

小贴士:ADAM4017 是研华科技的 16 位、8 通道模拟量输入模块,可以采集电压、电流等模拟量输入信号。

表2-9 “模拟量采集”界面控件的属性

控件名称 控件类型 属性名 属性值 控件功能
frm_analog 窗体 Text 模拟量采集
grp_show 组合框 Text 数据显示
grp_config 组合框 Text 配置
txt_light 文本框 输入/显示光照度值
txt_temperature 文本框 输入/显示温度值
txt_humidity 文本框 输入/显示湿度值
btn_get 按钮 Text 获取 控制数据获取
btn_Open 按钮 Text 开启 打开串口
com_port 下拉列表框 显示串口
lbl_light 标签 Text 光照度
lbl_temperature 标签 Text 温度
lbl_humidity 标签 Text 湿度

知识提炼(书 p39–41)

  1. Label 标签:显示用户不能编辑的文本或图像,不能获得焦点。表2-10 常见属性有 Name、Text、Enabled、AutoSize、BackColor(可设为 Color.Transparent)、BorderStyle(None / FixedSingle / Fixed3D)、Visible、ForeColor、Font、Image、TextAlign(仅在 AutoSize=false 时有效)。
  2. Button 按钮:
    • 表2-11 常见属性:Name、Text、Image、FlatStyle、DialogResult(OK、Cancel、Abort、Retry、Ignore、Yes、No)、Enabled、Visible、Font、BackColor、Image。
    • 表2-12 常见事件(书中表头误写为“方法”):Click、MouseDown、MouseUp。
    • 窗体的 AcceptButton(按 Enter 相当于点击该按钮)和 CancelButton(按 Esc 相当于点击该按钮)。
    • 示例“Label显示示例”(项目2\任务2)见表2-13:Form1 的 Text 为“Label显示”;btnDisplay 是按钮,Text 为“显示”;label1 是标签。代码 btnDisplay_Click 中执行 this.label1.Text = "显示内容";,运行后 label1 显示“显示内容”(图2-12、2-13)。

表2-13 “Label显示”界面控件的属性

控件名称 控件类型 属性名 属性值 控件功能
Form1 窗体 Text Label显示
btnDisplay 按钮 Text 显示
label1 标签

能力拓展

边想边做(书 p41–42)

  1. 用标签和文本框实现图片的显示与隐藏(图2-14)。点“显示”按钮显示图片,点“隐藏”按钮隐藏图片,图片素材在资源文件“项目2\图片\”中。界面:窗体标题“练习”,大图区域(企鹅图片),下方“显示”和“隐藏”两个按钮。实现上可用 Label.Image 加 Visible,或用 PictureBox。配套资源里没有“项目2\图片”文件夹,平台需要自备一张图片。
  2. 任务实施中已经能获取光照度、温度和湿度。要让模块实时获取这些环境值,可以用时间控件。按下面的提示实现:
    • 提示1:按表2-14 设置时间控件的关键属性(值由学生填)。

      属性 值
      名称 timer1
      Interval ?
      Enabled ?

      小贴士:Interval 表示多长时间执行一次 timer1_Tick 事件,单位是 ms,这里设多少比较合理?

    • 提示2:补全下面的代码。

      private void btn_get_Click(object sender,EventArgs e)
      {
          if(btn_get.Text=="获取")
          {
              timer1.Start(); //启动时间控件
              btn_get.Text="____";
          }
          else
          {
              timer1.____;
              btn_get.Text="____";
          }
      }
      
    • 提示3:参考“任务实施”中的代码,为 timer1 编写代码(即在 timer1_Tick 中调用 SetData 并刷新 3 个文本框)。

自主探究(书 p42):为模块增加以下功能:

  1. 实时采集大气压的数值。
  2. 每 2s 更新显示一次温度值、湿度值、光照度值和大气压值。

(仿真库的 ADAM4017ResModel 确实有 airPressureValue,还有 airQualityValue、CO2Value、windValue。adam4017Helper.Open 的签名为 Open(string PortName, int Temperature_Port=0, int Humidity_Port=1, int Light_Port=2, int AirPressure_Port=3, int AirQuality_Port=4, int CO2_Port=5, int Wind_Port=6)。所以这个练习在模拟器里可以做,模拟器应提供大气压数据。)

任务3 在LED显示屏中显示环境数据(书 p42–51 / PDF 45–54)

任务描述:结合 LED 显示屏推送,把温湿度传感器和光敏传感器的数据推送到 LED 屏显示。学生要学会使用 comboBox 和 TextBox。(有“扫码看视频”。)

任务实施

  1. 把 ADAM4017 和 LED 显示屏接到串口服务器,再把串口服务器接到 PC。
  2. 新建项目“LED推送模拟量”(项目2\任务3)。
  3. 把自定义控件 WinFormsDeviceControlLibrary.dll 加入工具箱。
  4. 复制 DeviceCtrlLibrary.dll 和 API 下的 dll 到 bin\Debug(没有硬件的读者用仿真 dll,不加 API)。
  5. 添加引用 DeviceCtrlLibrary.dll。
  6. 按图2-15 搭界面:顶部是黑色 LED 控件;左边 groupBox“数据显示”里有光照度、温度、湿度三个文本框和竖长的“获取”按钮;右边 groupBox“配置”里有“模拟量串口”下拉框、“LED串口”下拉框和“开启”按钮。
  7. 添加 using:System.IO.Ports、DeviceCtrlLibrary、DeviceCtrlLibrary.Model。
  8. 定义全局变量:bool IsOpen=false;、ADAM4017ResModel ResModel;、String LED_MSG="";。
  9. frm_LED_Load:清空 com_adamPort 和 com_ledPort,用 foreach 把 GetPortNames 的结果同时加入两个下拉框。
  10. btn_Open_Click:如果 !IsOpen,执行 adam4017Helper.Open(com_adamPort.Text) 和 ledHelper.OpenLedCom(com_ledPort.Text),btn_open.Text="关闭",IsOpen=true;否则执行 adam4017Helper.Close() 和 DeviceCtrlHelper.ledHelper.Close(),btn_open.Text="开启",IsOpen=false。
  11. btn_get_Click:先 LED_MSG="",SetData 后填好 3 个文本框,再拼接 LED_MSG += ("当前环境信息"+" "+"光照度:"+ResModel.lightValue+"lx温度:"+ResModel.temperatureValue+"℃湿度:"+ResModel.humidityValue+"%"); 然后 String Res = DeviceCtrlHelper.ledHelper.SendLedString(LED_MSG);,if(!(Res.IndexOf("Error")>=0)) led.setLedText(Res);

表2-15 “LED推送模拟量”界面控件的属性

控件名称 控件类型 属性名 属性值 控件功能
frm_LED 窗体 Text LED推送模拟量
grp_show 组合框 Text 数据显示
grp_config 组合框 Text 配置
txt_light 文本框 输入/显示光照度值
txt_temperature 文本框 输入/显示温度值
txt_humidity 文本框 输入/显示湿度值
btn_get 按钮 Text 获取 控制数据的获取
btn_Open 按钮 Text 开启 打开串口
com_adamPort 下拉列表框 显示串口
com_ledPort 下拉列表框 显示串口

(表中没有列 LED 控件,按代码它应叫 led;界面上也有光照度、温度、湿度和“模拟量串口”“LED串口”这些标签,表中同样没列。)

知识提炼(书 p45–49)

  1. comboBox 下拉列表框:点下三角或按 F4 展开,展开或折叠时触发 DropDownOpened / DropDownClosed。可以用 Items 静态添加,也可以用 Items.Add 动态添加,用 Items.Clear 清空。Text 属性是当前选中的内容,DropDownStyle 控制样式。

    • 示例“下拉设置字体样式”(项目2\任务3),图2-16 窗体“字体样式”:标签“字体样式”加下拉框,下方 label2 显示“字体[宋体]”。
    • Form1_Load:String[] type = {"宋体","黑体","楷体"},foreach 后执行 this.comboBox1.Items.Add(items),再 SelectedIndex = 0。
    • comboBox1_SelectedIndexChanged(书上写“找到 Click 事件”,实际是 SelectedIndexChanged):switch 0/1/2 时执行 this.label2.Font = new Font(comboBox1.Items[i].ToString(), 12); this.label2.Text = "字体["+ comboBox1.Items[i].ToString()+"]";
    • 运行效果见图2-17,例如“字体[黑体]”。

    表2-16 各控件相关属性及命名

    控件名称 控件类型 属性名 属性值
    Form1 窗体 Text 字体样式
    label1 标签 Text 字体样式
    label2 标签 Text 字体[字体名]
    comboBox1 下拉列表框 Items “宋体”“黑体”“楷体”

    (表里已经静态设了 Items,Form1_Load 又动态加了一遍,会出现重复项,属于书的错误。模拟器要能渲染 宋体、黑体、楷体 字体,即 new Font(name, size)。)

  2. TextBox 文本框:

    • 表2-17 常见属性:Text(默认最多 2048 字符,MultiLine 时 32KB)、MaxLength、MultiLine、HideSelection、ReadOnly、PasswordChar、ScrollBars、SelectionLength、SelectionStart、SelectedText、Lines、Modified、TextLength、WordWrap。
    • 表2-18 常见方法:AppendText、Clear、Focus、Copy、Cut、Paste、Undo、ClearUndo、Select(start,length)、SelectAll。
    • 表2-19 常见事件:GotFocus、LostFocus、TextChanged、Click。
    • 示例“添加用户”(项目2\任务3),图2-18:账号、密码、“添加用户”按钮和多行“用户信息”框。Form1_Load 中执行 this.tbox_Password.UseSystemPasswordChar = true;。btn_Add_Click 中执行 this.txtUserMessage.Text += "账号:"+user+"\t"+"密码:"+pass+"\r\n";,书中 user/pass 没有定义,应先取 tbox_Username.Text 和 tbox_Password.Text。运行效果见图2-19,例如“账号:test 密码:123”。

    表2-20 各控件相关属性及命名

    控件名称 控件类型 属性名 属性值
    Form1 窗体 Text 添加用户
    label1 标签 Text 账号:
    label2 标签 Text 密码:
    label3 标签 Text 用户信息:
    tbox_Username 文本框
    tbox_Password 文本框 UseSystemPasswordChar true
    txtUserMessage 文本框
    btn_Add 按钮 Text 添加用户

    (txtUserMessage 应设 Multiline=true。)

能力拓展

边想边做(书 p49–50):制作用户登录界面,点“登录”按钮后用 messagebox 显示用户名和密码(图2-20:窗体“登录界面”,标题“用户登录界面”,用户名和密码各一个文本框,一个“登录”按钮)。提示:设置密码文本框的相关属性。

表2-21 各控件的属性(书中原表就有问题,照录)

控件名称 控件类型 属性名 属性值
Form1 窗体 Text 登录界面
txtUsername 文本框 Text 用户名:
txtPassword 文本框 Text 密码:
label_title 标签 Text 用户登录界面
tbox_Username 文本框
btnLogin 按钮 Text 登录

(txtUsername 和 txtPassword 实际应是标签;表中漏了 tbox_Password(文本框),代码里用到了它。)

补充代码:

private void btnLogin_Click(object sender, EventArgs e)
{
    MessageBox.Show("用户名:"+this.tbox_Username.Text+"\r\n"+"密码:"+this.tbox_Password.Text);
}
private void Form1_Load(object sender, EventArgs e)
{
    this.tbox_Password.UseSystemPasswordChar = true;
    //设置文本框的UseSystemPasswordChar属性为True
}

运行效果见图2-21:输入 test 和 ***,弹窗显示“用户名:test / 密码:123”。

自主探究(书 p50–51):增加功能:用户名为“admin”且密码为“123456”时提示“登录成功”,否则提示“登录失败”(图2-22、图2-23)。注意图中输入的是 user1,看起来与题目要求不一致。

项目2 项目评价(书 p51–52 / PDF 54–55)

1. 项目评价指标(没有分值)

任务 评价指标 具体要求
任务1 LED显示屏的使用 按照教程完成界面和功能
任务1 制作LED显示计算结果 完成界面设计
任务1 制作LED显示计算结果 完成程序功能设计
任务2 窗体界面 界面布局合理,美观大方,符合使用者习惯
任务2 功能实现 环境值能正确获取
任务2 功能实现 环境值能正确显示
任务2 功能实现 实时时间控制正常
任务3 窗体界面 界面布局合理,美观大方,符合使用者习惯
任务3 功能实现 LED屏能正常显示
任务3 功能实现 环境值能正确推送到LED屏
任务3 功能实现 能实时推送信息
职业素养 注释书写规范 做到清晰、准确地描述内容。对于程序中复杂的部分必须有注释加以说明。注释量要适中,避免过多或过少导致阅读困难
职业素养 变量命名规范 选择有意义的英文进行命名,使变量名能清楚地表达其含义。尽量少使用人名、地名和汉语拼音。应使用那些约定俗成的缩写。多个单词组成的变量名,每个单词的首字母应该大写
职业素养 界面常用控件命名约定 窗体控件命名以控件名称缩写前3个字母开头,以具有代表含义的词来命名,其形式为:控件缩写(前3个字母)+命名(具有代表含义的单词)

2. 个人学习总结:“项目学习情况:”和“心得与反思:”两块空白。

项目2:书与参考工程的差异,以及模拟器要注意的点

  • 串口列表的填充时机不同:书中任务1 在 com_Port_Click 里填,任务2、3 在窗体 Load 里填;参考工程三个任务都在下拉框的 Click 事件里填(任务1 的 handler 名是 com_port_Click,绑定到 com_Port.Click)。模拟器的 ComboBox 必须支持 Click 事件,要在点击、展开之前触发,这样 Items 才能在展开前更新。SerialPort.GetPortNames() 需要返回虚拟串口列表,例如 COM1–COMn。
  • IsOpen 写法不同:书中是 bool IsOpen=false 加 if(!IsOpen){...IsOpen=True};参考工程是 bool IsOpen = true,先 IsOpen = !IsOpen 再判断,事件内容都包在 try/catch 中。书上写成了 True/False(首字母大写),C# 编译会报错,模拟器的编译错误提示要能指出这一点。
  • 控件名差异:
    • 任务2、3 参考工程的窗体名都是 Form1(书为 frm_analog / frm_LED)。
    • 组合框名是 groupBox1/2(书为 grp_show / grp_config),标签是 label1–5(书为 lbl_*)。
    • 任务3 的按钮在书上和参考工程里都叫 btn_open(小写 o),但表2-15 写的是 btn_Open。
    • LED 自定义控件实例名为 led(类型 WinFormsDeviceControlLibrary.LED),表2-1 写的是“LED”。
  • DeviceCtrlHelper.ledHelper.Close():书中任务3 第10步调用了它,但参考工程没有调用,《设备控制类库使用说明》中 LED 部分也只列了 OpenLedCom(string) 和 SendLedString(string),仿真 dll 很可能没有这个方法。模拟器可以二选一:提供一个空实现以兼容书上的代码,或者给出明确的编译错误。建议提供空实现。
  • SendLedString 的约定:成功时返回传入的字符串,失败时返回 "Error:发送失败"(见使用说明)。LED 控件 setLedText(string) 负责显示,控件内部还有 LedTimer,推测会滚动显示。
  • 工具箱里的 4 个自定义控件:Alarm、Lamp、LED、ShowMsg(图2-5)。ShowMsg 在本范围内没有出现,从 dll 符号看它包含 txt_showMsg、btn_red、btn_green、btn_Clear 和 AddText,后续项目可能用到。
  • 任务2 能力拓展需要 Timer(Interval、Enabled、Start/Stop、Tick)和图片显示(Label.Image 或 PictureBox,以及 Visible)。
  • 任务3 知识提炼需要 Font 构造函数和中文字体,还需要 UseSystemPasswordChar、\t 制表符的显示,以及多行 TextBox。
  • 知识提炼中的 SerialPort 直接收发示例(Base64 + Unicode)超出了“学生不学协议”的范围,可以只作阅读材料。如果要能运行,需要模拟 new SerialPort()、Open、WriteLine、ReadLine 和 Close。

项目3 智慧校园系统——智慧路灯控制模块(书 p53–72 / PDF 56–75,本部分只覆盖到书 p62)

项目概述:ZigBee 是一种近距离、低复杂度、低功耗、低速率、低成本的双向无线通信技术。智慧路灯控制模块借助 ZigBee 远程控制路灯开关。

学习目标

  • 了解智慧路灯控制模块在提升校园管理效率中的作用与重要性。
  • 熟悉 ZigBee 协调器、串口服务器等硬件设备的连接与配置方法。
  • 掌握 C# 在物联网编程中的应用,包括窗体应用程序的创建、控件的使用与事件处理等。
  • 了解并学会使用 DeviceCtrlLibrary 等自定义控件及 API 接口。
  • 具备环境监测与联动控制的能力。
  • 具备良好的团队合作精神与沟通能力,能够与团队成员有效协作,共同完成任务。
  • 面对智慧路灯控制系统开发中遇到的问题,能够独立思考、分析问题,并提出有效的解决方案。

任务1 使用ZigBee技术控制路灯开关(书 p53–58 / PDF 56–61)

任务描述:用 C# 写程序,通过 ZigBee 无线技术实现路灯的无线开关控制。学生要了解 ZigBee 技术,学会使用 if 语句。(有“扫码看视频”。)

任务实施

  1. 把 ZigBee 协调器的串口接到 PC(没有硬件的跳过)。
  2. 新建项目“ZigBee控制路灯”,保存位置为 项目3\任务1。参考工程和后文路径都叫“ZigBee路灯”。
  3. 把自定义控件 WinFormsDeviceControlLibrary.dll 加入工具箱。
  4. 复制 DeviceCtrlLibrary.dll 和 API 下的 dll 到 bin\Debug。
  5. 添加引用 DeviceCtrlLibrary.dll(没有硬件的读者用仿真 dll)。
  6. 按图3-1 搭界面:左边是灯泡(Lamp 控件),下面有“开”“关”两个单选按钮;右边 groupBox“配置”里有“路灯”下拉框和“打开”按钮。
  7. 添加 using:System.IO.Ports、DeviceCtrlLibrary。
  8. 定义方法:
    //控制开关灯
    private void LampOnOff(int RelayNum, bool IsOpen)
    {
        DeviceCtrlHelper.zigBeeHelper.OnOff(RelayNum, IsOpen);
        lamp1.OnOff(IsOpen);
    }
    
  9. Form1_Load:清空 com_port,再加入 GetPortNames 的结果。
  10. btn_Open_Click:如果 btn_open.Text.Equals("打开"),执行 zigBeeHelper.Open(com_port.Text) 并把按钮文字改为“关闭”;else if (btn_open.Text.Equals("关闭")) 时执行 zigBeeHelper.Close() 并改回“打开”。
  11. “开”单选按钮的 CheckedChanged:rb_Open_CheckedChanged 中调用 LampOnOff(1, true)。
  12. “关”单选按钮的 CheckedChanged:调用 LampOnOff(1, false)。书中这个方法名误写为 rb_Open_CheckedChanged,应为 rb_Close_CheckedChanged。

表3-1 “ZigBee路灯”界面控件的属性

控件名称 控件类型 属性名 属性值 控件功能
Form1 窗体 Text ZigBee路灯
groupBox1 组合框 Text 配置
lamp1 自定义路灯控件
rb_Open 单选按钮 Text 开 开灯
rb_Close 单选按钮 Text 关 关灯
com_port 下拉列表框 获取PC串口
btn_Open 按钮 Text 打开 打开串口
lbl_Lamp 标签 Text 路灯

知识提炼(书 p56–57):if 语句

  • 两种形式:if(expression) statement 和 if(expression) statement else statement。
  • 示例:if(i>1) y=x/i; else { x=i; y=x*x; }。
  • 悬空 else:没有大括号时,else 与最近的、还没有 else 的 if 关联;加大括号后 else 属于外层 if。

能力拓展

边想边做(书 p57–58)

  1. 闰年判断:在文本框输入年份,点“判断”按钮。是闰年则在右边标签显示“是闰年”,否则显示“不是闰年”。界面见图3-2 到图3-4:窗体标题“闰年判断”,标签“请输入年份”,一个文本框,右侧标签初始为“是否闰年?”,下方“判断”按钮。示例:1700 显示“不是闰年”,2000 显示“是闰年”。
    • 提示1:字符型数值不能直接赋给数值型变量,要先转换,如 int a = int.Parse("12") 或 int a = Convert.ToInt16("12")。
    • 提示2:闰年条件是能被 4 整除但不能被 100 整除,或者能被 400 整除。
  2. 路灯按时间自动开关:要求 18:00 到次日 7:00 开灯,其余时间关灯。用时间控件 Timer(从工具箱选取)完成,每分钟判断一次;程序运行后,打开串口时开启时间控件。
    • 提示1:按表3-2 设置时间控件的关键属性(值留空由学生填)。
      属性 值
      名称 timer1
      Interval ____
      Enabled ____
    • 提示2:补全代码,再双击时间控件 timer1 进入代码窗口输入。
      private void timer1_Tick(object sender, EventArgs e)
      {
          int h = DateTime.Now.Hour;//获取当前系统时间小时数
          if( ____ ) //判断时间
              ____      //开关灯
          else
              ____      //开关灯
      }
      
      参考答案:Interval=60000;h >= 18 || h < 7;LampOnOff(1, true); / LampOnOff(1, false);。模拟器要用到 DateTime.Now,最好允许在模拟器里设置或加速虚拟时钟,否则学生白天测不到开灯。

自主探究(书 p58):为模块增加以下功能:

  1. 实时采集大气压的数值。
  2. 根据大气压控制路灯开关。

任务2 实现人体红外传感器和路灯联动(书 p58–? / PDF 61–?,本部分读到书 p62)

任务描述:人体红外传感器基于多普勒原理,人靠近感应区域时,开关会无接触、无压力、无火花地迅速发出电气指令。本任务模拟楼梯灯:有人经过时楼梯灯亮,无人时自动熄灭。学生要学会使用单选按钮和复选框,理解数组的作用。(有“扫码看视频”。)

任务实施

  1. 把数字量采集器 ADAM4150 和 ZigBee 协调器接到串口服务器,再把串口服务器接到 PC。
  2. 新建项目“人体红外ZigBee联动”(项目3\任务2)。
  3. 添加自定义控件 WinFormsDeviceControlLibrary.dll。
  4. 复制 DeviceCtrlLibrary.dll 和 API 下的 dll 到 bin\Debug。
  5. 添加引用 DeviceCtrlLibrary.dll(没有硬件的读者用仿真 dll)。
  6. 再添加一次自定义控件(书中这一步与第3步重复)。
  7. 按图3-5 搭界面:左边是灯泡(lamp1),下方“开”“关”两个按钮;右边 groupBox“配置”里有“路灯”下拉框、“环境采集”下拉框、“人体开关”复选框和“打开”按钮。
  8. 添加 using:System.IO.Ports、DeviceCtrlLibrary。
  9. Form1_Load:分别清空 com_lamp 和 com_environment,并用 GetPortNames 填充。
  10. LampOnOff(int RelayNum, bool IsOpen):与任务1 相同,即 zigBeeHelper.OnOff 加 lamp1.OnOff。
  11. btn_open_Click:与任务1 相同,按 Text 在“打开”和“关闭”之间切换,用的是 zigBeeHelper.Open(com_lamp.Text) 和 Close()。
  12. ch_bodyInfrared_Click(复选框“人体开关”的 Click 事件):
    • 如果 ch_bodyInfrared.Checked:执行 DeviceCtrlHelper.adam4150Helper.Open(com_environment.Text),timer1.Start(),并把 btn_lampOpen.Enabled 和 btn_lampClose.Enabled 设为 false(禁用手动开关)。
    • 否则:执行 adam4150Helper.Close(),timer1.Stop(),两个按钮的 Enabled 设回 true。
  13. btn_lampOpen_Click:调用 LampOnOff(1, true)。
  14. btn_lampClose_Click:调用 LampOnOff(1, false)。书中第14步标题误写为选中“开”按钮。
  15. timer1_Tick(书上写成“双击 timer1 进入 Click 事件”,实际是 Tick):
    //时间控件实时判断是否感应到人,如果感应到人则开路灯,否则关闭
    private void timer1_Tick(object sender, EventArgs e)
    {
        if(DeviceCtrlHelper.adam4150Helper.SetData().bodyInfraredValue)
            LampOnOff(1, true);
        else
            LampOnOff(1, false);
    }
    

表3-3 “人体红外ZigBee联动”界面控件的属性

控件名称 控件类型 属性名 属性值 控件功能
Form1 窗体 Text 人体红外ZigBee联动
groupBox1 组合框 Text 配置
lbl_lamp 标签 Text 路灯
lbl_environment 标签 Text 环境采集
com_lamp 下拉列表框 显示路灯串口
com_environment 下拉列表框 显示环境串口
ch_bodyInfrared 复选框 Text 人体开关 控制人体红外传感器是否启动
btn_Open 按钮 Text 打开 打开串口
lamp1 自定义路灯控件
btn_lampOpen 按钮 Text 开 手动控制开灯
btn_lampClose 按钮 Text 关 手动控制关灯
timer1 时间控件 Interval 5000
timer1 时间控件 Enabled False

知识提炼(书 p61– ,本部分读到 p62)

  1. RadioButton 单选按钮:可以显示文本、Image 或两者。同一容器(Form、GroupBox、Panel)中的 RadioButton 构成一组,组内互斥;CheckBox 可以多选。Checked 获取或设置状态,Appearance 可以把外观设成切换式按钮或标准选项按钮。表3-4 常见属性:Appearance、AllowDrop、AutoCheck、AutoSize、BackColor、BackgroundImage、BackgroundImageLayout、Bottom、CanFocus、CanSelect、Checked、Enabled、Focused、ForeColor、FontHeight、Image、Text、TextAlign、Visible。

    • 单选按钮示例“单选按钮”(项目3\任务2),图3-6 窗体“设置字体颜色”:左侧竖排 4 个单选按钮(黑色、红色、蓝色、黄色),右侧 label1 显示“字体颜色[黑色]”。

    表3-5 “设置字体颜色”窗体界面各控件相关属性及命名

    控件名称 控件类型 属性名 属性值
    Form1 窗体 Text 设置字体颜色
    radioButton_Black 单选按钮 Text 黑色
    radioButton_Red 单选按钮 Text 红色
    radioButton_Blue 单选按钮 Text 蓝色
    radioButton_Yellow 单选按钮 Text 黄色
    label1 标签 Text 字体颜色[颜色名]

>>> 本部分在此结束(PDF 65 / 书 p62)。下一页(PDF 66 / 书 p63)应接着给出该示例的代码步骤,然后是 CheckBox 复选框知识点、任务2 的能力拓展和自主探究,以及项目3 评价(书 p71 / PDF 74)。<<<

项目3(到书 p62 为止):书与参考工程的差异,以及模拟器要注意的点

  • 任务1 控件不同:书中用单选按钮 rb_Open/rb_Close 和 CheckedChanged 开关灯;参考工程 项目3/任务一/ZigBee路灯 用的是按钮 btn_lampOpen/btn_lampClose 和 Click,与任务2 一样。参考工程没有 rb_*,groupBox 名为 groupbox1(小写 b),标签 label1 的 Text 为“路 灯”。另外书中表3-1 写 btn_Open,代码写 btn_open,参考工程也是 btn_open。
  • 用单选按钮做开关时要注意:CheckedChanged 在选中和取消选中时都会触发。rb_Open 被取消(因为选了 rb_Close)时,如果只写 LampOnOff(1,true) 就会出问题。书上的写法只在“两个 handler 各自绑定、不判断 Checked”的情况下“恰好能用”:选“关”时两个事件都会触发,先触发谁决定最终状态。模拟器必须按真实 WinForms 顺序触发 CheckedChanged:先是被取消的那个,再是被选中的那个。这样书上的代码会在“开灯”和“关灯”各执行一次后停在正确状态。
  • 参考工程中串口列表在 com_lamp_Click / com_environment_Click(ComboBox 的 Click)中填充,书中则在 Form1_Load 中填充。参考工程的所有 handler 都包了 try/catch。
  • 参考工程的 timer1 是 Interval=5000,Enabled 为默认 false,与表3-3 一致。
  • zigBeeHelper.OnOff(RelayNum, IsOpen):使用说明写继电器通道为 0~1,书和参考工程都用 1。zigBeeHelper.Close() 在使用说明里误拼为 Colse(),仿真 dll 里实际是 Close。
  • adam4150Helper.SetData() 返回 ADAM4150ResModel,包含 bool 型的 bodyInfraredValue、fireValue、smokeValue、infraredValue。adam4150Helper.Open(string PortName, int BodyInfrared_Port=0, int Fire_Port=1, int Smoke_Port=2, int Infrared_Port=3);另有 adam4150Helper.OnOff(int DO, bool IsOpen)(DO 0~4)。模拟器要提供“人体红外”的虚拟触发开关,学生用它模拟有人经过。
  • Lamp 控件的方法是 lamp1.OnOff(bool),图片资源在 开发文档/图片资源/控件图片/LampOn.png 和 LampOff.png。
  • 复选框用的是 Click 事件而不是 CheckedChanged。Click 触发时 Checked 已经切换过了,模拟器要保证这个时序。
  • 任务1 能力拓展用到 DateTime.Now.Hour;自主探究“根据大气压控制路灯”要用 adam4017 的 airPressureValue(string 类型,要 Parse),所以需要同时打开 ADAM4017 和 ZigBee 两个虚拟串口。