Files
IOT_SIM/docs/教材摘要/3-项目6后半到项目8.md
T
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

65 KiB
Raw Blame History

《物联网编程与应用 第2版》摘要 第3部分(PDF 第126–187页)

页码对照

PDF 页 书页 内容
126–127 124–125 项目6 任务2(中途开始)末尾:步骤16运行结果;知识提炼 1–4
128–129 126–127 项目6 任务2 知识提炼5、能力拓展;项目6 任务3 开始
130–132 128–130 项目6 任务3 实施 + 知识提炼
133 131 项目6 项目评价
134 133(无页码) 项目7 首页 + 任务1 任务描述(书第132页应是空白页,扫描件里没有)
135–141 134–140 项目7 任务1
142–149 141–148 项目7 任务2(PDF 149 / 书148 上半页是任务2的自主探究)
149–156 148–155 项目7 任务3
157 156 项目7 项目评价
158 157(无页码) 项目8 首页 + 任务1 任务描述
159–160 158–159 项目8 任务1;任务2 开始
160–165 159–164 项目8 任务2
165–175 164–174 项目8 任务3(书174上半页)
175–181 174–180 项目8 任务4
182–186 181–185 项目8 任务5 + 项目评价(书185)
187 186 项目8 项目评价(续)+ 个人学习总结

页码规则:PDF 126–133 时,书页 = PDF − 2;PDF 134–187 时,书页 = PDF − 1。 扫描件到 PDF 187(书186)就结束了,没有「参考文献」页,也没有封底。


项目6 智慧校园系统——数据存储模块(只覆盖结尾部分)

任务2(中途开始;任务名称在我负责的范围之前)

PDF 126 开头是「修改」按钮 Click 代码的结尾 MessageBox.Show("修改失败:" + ex.ToString()); } },接着是步骤16)运行该程序,图6-11「运行结果」。窗体标题是「校园灾情数据存储」,各区域如下:

  • 「配置」组:串口下拉框(COM1)+「关闭」按钮
  • 「数据显示」组:光照度/温度/湿度文本框,「获取」「保存」「修改」「删除」按钮
  • 「历史记录」组:DataGridView,列为 编号/温度/湿度/光照度/时间

从项目评价表看,这个任务的内容是显示光照度、温度、湿度,并把数据存进数据库。参考工程是 项目6/任务二/校园数据存储,与书上一致:btn_Open / btn_get / btn_save / btnUpdate / btnDelete。

知识提炼(书124–126)

  1. insert 插入语句:insert into 表名 (列1, 列2, ...) values (值1, 值2, ...);。示例表 user 有 id(自增主键)、username varchar(50)、birthday date 三个字段:
    • 完整插入 insert into user (id, username, birthday) values (1, '张三', '1999-09-09');
    • 部分插入 insert into user (username) values ('李四');(id 自增)
    • 简写插入 insert into user values ('王五', '1910-10-10');(按字段顺序填写,跳过自增 id)
  2. update 更新语句:update 表名 set 列1=新值1, 列2=新值2, ... [where 条件];。示例:update user set birthday='2016-01-01' where username='张三';;还有 T-SQL 特有写法 update top (3) user set birthday='2000-01-01';
  3. delete 删除语句:delete from 表名 [where 条件];。示例:delete from user where username='张三';、delete top (5) from user;、delete from user;
  4. ADO.NET 对象模型简介:ADO.NET 由「.NET 数据提供程序」和「数据集 DataSet」两部分组成。表6-2 ADO.NET 核心组件:
对象 说明 特点
Connection 建立与特定数据库的连接 可以自己创建,也可由其他对象自动产生(如 DataAdapter)
Command 对数据源执行命令 通过 Connection 对象下达命令,Connection 的指向决定操作对象
DataAdapter 从数据源中检索数据并填充 DataSet 中的表 DataAdapter 填充到 DataSet 中的是断开连接的脱机数据
DataReader 从数据库中检索只读的数据流 DataReader 读取数据必须在维持数据库连接的前提下进行
DataSet 在内存中缓存从数据源中检索到的数据 独立于其他数据源
  1. ADO.NET 中的 SQL 操作类
    • SqlConnection:表示一个打开的 SQL Server 连接,不能被继承;OLE DB 数据源要用 OleDbConnection。有两个构造函数:SqlConnection() 和 SqlConnection(String 连接字符串)。用完后关闭。
    • SqlCommand:执行 T-SQL 语句或存储过程。有四个构造函数:()、(String)、(String, SqlConnection)、(String, SqlConnection, SqlTransaction)。
    • SqlDataReader:从上往下读取只读数据。必须通过 SqlCommand.ExecuteReader() 创建,不能直接用构造函数。使用期间连接被它独占,必须调用 Close() 后连接才能做其他操作。

能力拓展

边想边做(书126):

通过「任务实施」的操作步骤,对已经实现了的环境数据添加一个图片框,以获取监控画面,并将环境数据与监控画面每5min添加入数据库。环境数据界面如图6-12所示。

图6-12「环境数据界面」,窗体标题「校园灾情数据存储」:

  • 左上「数据显示」组:光照度、温度、湿度 3 个文本框;「获取」「保存」按钮
  • 左中「配置」组:串口下拉框 +「开启」按钮
  • 右侧「监控图像采集」组:一块大图片框
  • 底部「历史记录」组:DataGridView(空)

自主探究(书127):

在「边想边做」的基础上,为程序增加下列功能:添加一个文本框 TextBox(Name 属性值为 lightValue)用于输入更新条件,当表中「light」的值超过 TextBox 的值时,将数据添加入数据表。

任务3 查询校园安防数据(书127–130 / PDF 129–132)

任务描述:在任务1、任务2的基础上,实现灾情数据按时间段查询。学生通过这个任务熟悉 select 语句以及 DateTimePicker、DataGridView 两个控件。

任务实施

  1. 按图6-13「查询图片窗体界面」建 Form1(标题「查询校园灾情数据」):顶部一行「开始日期 [DateTimePicker] 结束日期 [DateTimePicker] [查询]」,下面「历史记录」组框里放 DataGridView。

表6-3 查询图片窗体界面控件属性:

控件名称 控件类型 属性名 属性值 控件功能
LblStartTime 标签 Text 开始日期 开始日期
LblEndTime 标签 Text 结束日期 结束日期
StartTime 日期时间控件 Datetime 时间 起始时间
EndTime 日期时间控件 Datetime 时间 结束时间
btnCx 按钮 Text 查询 查询
groupBox4 组合框 Text 历史记录 组合框
dataGridView1 数据网格控件
  1. 引用 System.Data.SqlClient、System.IO.Ports。
  2. 声明 5 个全局变量:连接字符串 sqlConnectString、SqlConnection mySqlConnection、SqlCommand mySqlCmd、SqlDataAdapter sqldata、DataSet ds。
  3. 写 btnCx_Click:打开连接,执行 select ID as '编号',temperature as '温度',humidity as '湿度',light as '光照度',addtime as '时间' from CampusEnvironment where addtime BETWEEN '<StartTime.Value>' AND '<EndTime.Value>',用 SqlDataAdapter.Fill(ds) 填充,再 dataGridView1.DataSource = ds.Tables[0].DefaultView,最后关闭连接。
  4. 运行,选好起止日期后点「查询」,结果见图6-14(示例数据为 2024/1/16–2024/1/23 共 9 行)。

知识提炼(书128–130)

  1. select 查询语句:SELECT 列名称 FROM 表名称 或 SELECT * FROM 表名称。小贴士:SQL 对大小写不敏感。示例:SELECT * FROM user、SELECT id,username FROM user。表6-4 where 子句运算符:
运算符 描述 实例
= 等于 查询表中姓名为张三的生日数据:SELECT * FROM user where username='张三'
<> 不等于。在 SQL 的一些版本中可写成 != 查询表中姓名不是张三的生日数据:SELECT * FROM user where username<>'张三'
> 大于 查询表中生日为2000年后的数据:SELECT * FROM user where year(birthday)>2000
< 小于 查询表中生日为2000年前的数据:... where year(birthday)<2000
>= 大于等于 查询表中生日为2000年(含2000)后的数据:... where year(birthday)>=2000
<= 小于等于 查询表中生日为2000年(含2000)前的数据:... where year(birthday)<=2000
between 在某个范围内 查询表中生日为2000—2006年之间的数据:... where year(birthday) between 2000 in 2006(书上原文就是 in,正确写法应为 and)
like 搜索某种模式 查询表中姓张的数据:SELECT * FROM user where username like '张%'

逻辑运算符:AND 要求所有条件都成立,OR 只要一个条件成立即可。 2. SqlDataAdapter 类:DataSet 与 SQL Server 之间的桥接器,通过 Fill(让 DataSet 跟数据源一致)和 Update(让数据源跟 DataSet 一致)工作。 3. DataSet 类:内存中的临时数据库,包含 DataTable、DataView。第 i 行第 j 列的访问方式是 ds.Tables[0].Rows[i].ItemArray[j],索引从 0 开始。 4. DataGridView 控件:表格形式显示和编辑数据;绑定时设置 DataSource,数据源含多个表时再设置 DataMember。 5. DateTimePicker 控件:Format 枚举(DateTimePickerFormat)可取 Long / Short / Time / Custom,选 Custom 时用 CustomFormat 字符串;Text 返回文本,Value 返回 DateTime;MinDate / MaxDate 限制范围;点下箭头弹出日历;ShowUpDown 显示上下箭头。三种常用设置:

  • 只显示时间:Format=Time,ShowUpDown=true
  • 只显示时分(如 7:30):Format=Custom,CustomFormat=HH:mm
  • 显示日期和时间:Format=Custom,CustomFormat=yyyy:MM:dd HH:mm:ss(书上原文如此)

任务3没有「能力拓展」。

项目6 项目评价(书131)

任务 评价指标 具体要求
任务1 功能实现 能正确登录数据库;能正确新建数据库;能正确添加数据
任务2 窗体界面 界面布局合理,美观大方,符合使用者的习惯
任务2 功能实现 能正确显示光照度、温度、湿度;能利用数组正确储存数据
任务3 窗体界面 界面布局合理、美观大方,符合使用者的习惯
任务3 功能实现 能实现按日期查询灾情数据
职业素养 代码书写规范 应该遵从结构化的要求,采用缩进的格式
职业素养 注释书写规范 做到清晰、准确地描述内容。对于程序中复杂的部分必须有注释加以说明。注释量要适中,避免过多或过少导致阅读困难
职业素养 变量命名规范 选择有意义的英文进行命名,使变量名能大致清楚地表达其含义。尽量少使用人名、地名和汉语拼音。应使用那些约定俗成的缩写。多个单词组成的变量名,每个单词的首字母应该大写
职业素养 界面常用控件命名约定 窗体控件命名以控件名称缩写前3个字母开头,以具有代表含义的词来命名,其形式为:控件缩写(前3个字母)+命名(具有代表含义的单词)
职业素养 数据库规范 符合数据库命名、设计规范,做到SQL编码优化规范

评价表没有分值,只有指标和要求,后面跟「个人学习总结」(项目学习情况 / 心得与反思,空白待填)。项目7、项目8 的评价表格式相同。

项目6 书与参考代码的差异 / 模拟器要点

  • 任务3 书上代码漏了 ds = new DataSet();,直接 sqldata.Fill(ds) 会抛空引用异常。参考代码 项目6/任务三/校园数据存储/Form1.cs 里有这一行。
  • 列别名:书上是 light as '光照度',参考代码是 light as '光照'。
  • 连接字符串:书上是 Data Source= PC-20150908SCJF\SQLSERVER1026;Initial Catalog=环境数据表;User ID=sa;Password=123456,参考代码是 Data Source=.。数据库名「环境数据表」,表名 CampusEnvironment,字段 ID / temperature / humidity / light / addtime。
  • 模拟器的 SQL 层需要支持:BETWEEN '日期字符串' AND '日期字符串'(拼接的是 DateTime.ToString(),即中文区域格式 2024/1/16 14:42:00)、as '中文别名'、top (n) 用在 update/delete 上、year() 函数、like '张%'。另外要支持 SqlCommandBuilder + DataAdapter.Update(任务2的「修改」功能),以及 DataGridView 绑定 DataView。
  • 书上的 SQL 知识点用的是 T-SQL 方言(top (n)、year())。如果后端用 SQLite,需要转换或模拟这些语法。

项目7 智慧校园系统——情景控制模块(书133–156)

项目概述:情景控制就是用智能化技术实现自动控制,集成摄像头、温湿度传感器、语音识别器、智能电器等设备。本项目模拟智慧教室、智慧宿舍、智慧会议室三个场景,采集光照度、温湿度等数据,根据时间和环境联动灯光、窗帘、广播。

学习目标:

  • 理解程序项目文件的结构
  • 能根据需求设计交互 UI 窗体,并实现与数据的联动
  • 能用 Button、Timer、TrackBar、PictureBox 等组件对物联网设备做简单控制
  • 培养分析和解决问题的能力
  • 养成耐心细致、严谨扎实的工作作风
  • 增强职业认同感,提升职业素养,树立职业自信心

任务1 实现智慧教室(书133–140 / PDF 134–141)

任务描述:在上课模式下,根据光照度自动开关灯,根据温度自动开关空调(空调用 TrackBar 模拟)。

任务实施

  1. 把 4017(模拟量采集器)和 4150(数字量采集器)的串口接到 PC(没有硬件可以跳过)。
  2. 新建 WinForms 项目「教室情景模式」,保存到 D:\物联网编程与应用\项目7\任务1\。
  3. 把 ADAM4017、ADAM4150 需要的 DeviceCtrlLibrary.dll 和「API」文件夹里的所有 dll 复制到 ...\教室情景模式\bin\Debug\。
  4. 添加引用 DeviceCtrlLibrary.dll。没有硬件的读者改为添加「仿真动态库文件」,不需要 API 文件夹里的 dll。
  5. 项目属性 → 资源 →「添加资源」,加入 switch_off.png、switch_on.png。
  6. 按图7-1建窗体(标题「智慧教室情景控制」):
    • 顶部「模式选择」组:标签「智慧教室将开启」+「启用」按钮
    • 左侧「采集量」组:串口下拉框,光照度/温度/湿度文本框
    • 右侧「状态栏」组:「空调」+ 开关图片 + TrackBar(两端标 18℃ / 32℃);「灯光」+ 开关图片 + 灯泡控件

表7-1 窗体控件属性:

控件名称 控件类型 属性名 属性值 控件功能
frm_control 窗体 Text 智慧教室情景控制
lbl_mode 标签 Text 智慧教室将开启
btn_apply 按钮 Text 启用 开启、关闭模式
grp_input 组合框 Text 采集量
lbl_import 标签 Text 串口
lbl_light 标签 Text 光照度
lbl_temperation 标签 Text 温度
lbl_humidity 标签 Text 湿度
com_import 下拉列表框 设置采集串口
txt_light 文本框 显示光照度
txt_temperation 文本框 显示温度
txt_humidity 文本框 Text 显示湿度
grp_status 组合框 Text 状态栏
lbl_light 标签 Text 灯光
lbl_airconditione 标签 Text 空调
lamp 灯泡 模拟电灯开关
trb_airconditione 滑动条 模拟空调温度
lbl_lbl_airconditionmin 标签 Text 18℃
lbl_airconditionmax 标签 Text 32℃
pic_lightsStatus 图片框 Image Switch_off.png 控制并显示电灯状态
pic_airconditioneStatus 图片框 Image Switch_off.png 显示空调状态
timer 时间控件 Interval 2000 实现实时采集

(表中有书上原样的问题:lbl_light 出现了两次,「lbl_lbl_airconditionmin」也是原文;后两行文本框名书上写作 txt_temperation,代码里用的是 txt_temperature。)

  1. 引用 System.IO.Ports、DeviceCtrlLibrary、DeviceCtrlLibrary.Model。
  2. 定义 bool IsOpen、IsLampOpen、IsairconditioneOpen,以及 ADAM4017ResModel ResModel。
  3. frm_control_Load:用 SerialPort.GetPortNames() 填充 com_import。
  4. btn_apply_Click:第一次点击打开 4017/4150 串口,按钮改为「关闭」,lbl_mode 改为「智慧教室已开启」,启动 timer;再次点击关闭串口、停止 timer、恢复文字。
  5. timer_Tick:调用 adam4017Helper.SetData() 采集并显示。光照 <400 时开灯(图片 switch_on、lamp.OnOff(true)、adam4150Helper.OnOff(1,true)),>800 时关灯。温度 >30 时开空调、trb=26;<5 时开空调、trb=25;其他情况关空调、trb=18。
  6. pic_lightsStatus_Click:手动切换灯。

知识提炼

  1. TrackBar 控件:横向或纵向拖动来设置值,类似系统音量条。

表7-2 TrackBar 常见属性:

属性 说明
Orientation 获取或设置轨道方向,默认 Horizontal(横向,左右拖动),可改为 Vertical(纵向,上下拖动)
TickFrequency 获取或设置滑块旁刻度的间隔,默认为1
TickStyle 获取或设置刻度位置,默认 BottomRight(在底部,从左往右递增),可改为 TopLeft(在顶部,从右往左递增),还有 Both(两种都要)、None(无刻度)
Enabled 获取或设置滑块是否可拖动,默认 true,可改为 false 表示不能拖动
Maximum 滑块条最大端的值,默认10
Minimum 滑块条最小端的值,默认0
LargeChange 长距离移动(鼠标单击轨道让滑块靠近)时每次改变的值,默认5
SmallChange 鼠标拖动滑块或键盘方向键移动时每跳的最小值,默认1
Value 当前滑块所表示的值,默认0
Visible 滑动条是否可见,默认 true,可改为 false 表示隐藏

表7-3 TrackBar 常见事件:

事件 说明
Scoll(原文拼写,应为 Scroll) 用户拖动滑块、按下箭头或单击轨道改变值时触发
ValueChanged 该事件仅在用户通过拖动滑块更改值时触发(书上原文;实际上代码修改 Value 时也会触发)
  1. TrackBar 显示案例(书中独立的小练习,适合做成平台题目):新建「TrackBar显示案例」项目,保存到 D:\物联网编程与应用\项目7\任务1\。窗体(图7-2)中间是一个 TrackBar,左右两端标签 0 和 10,下方一个标签显示当前值。

表7-4「TrackBar显示」界面控件的属性:

控件名称 控件类型 属性名 属性值 控件功能
Form1 窗体 Text TrackBar显示
trackBar1 滑动条
label1 标签 Text 0
label2 标签 Text 10
label3 标签 Text 0 显示当前TrackBar控件对应的数值

代码:private void trackBar1_Scroll(...) { label3.Text = Convert.ToString(trackBar1.Value); }。运行后拖动滑块,label3 显示 6 等数值(图7-3)。

能力拓展

边想边做(书139–140):

通过「任务实施」的操作步骤,已经实现了智慧教室上课模式的基本功能。现在在此基础上实现考试模式。通过添加考试模式,要求在考试模式下窗帘全开(用 TrackBar 模拟开关,0 为全开,10 为全关),光照度不够 200 时开启电灯。请根据下列提示,实现上述功能。 提示:根据以下提示,完善 timer_Tick 事件。

private void timer_Tick(object sender, EventArgs e)
{
    ResModel = DeviceCtrlHelper.adam4017Helper.SetData();//采集数据
    txt_light.Text = ResModel.lightValue;
    txt_temperature.Text = ResModel.temperatureValue;
    txt_humidity.Text = ResModel.humidityValue;
    if(com_mode.Text=="上课模式")//上课模式,与任务实施部分相同
    {
        if (Convert.ToInt16(ResModel.lightValue) < 200)//光照度过低则打开灯
        {
            pic_lightsStatus.Image = Properties.Resources.switch_on;
            IsLampOpen = true;
            lamp.OnOff(true);//打开界面灯
            DeviceCtrlHelper.adam4150Helper.OnOff(1,true);//打开1通道灯
        }
        else if (Convert.ToInt16(ResModel.lightValue) > 800 )
        {
            pic_lightsStatus.Image = Properties.Resources.switch_off;
            IsLampOpen = false;
            lamp.OnOff(false);//关闭界面灯
            DeviceCtrlHelper.adam4150Helper.OnOff(1, false);//关闭1通道灯
        }
        if (Convert.ToInt16(ResModel.temperatureValue) > 30 )
        {
            pic_airconditioneStatus.Image = Properties.Resources.switch_on;
            IsairconditioneOpen = true;
            trb_airconditione.Value = 26;
        }
        else if (Convert.ToInt16(ResModel.temperatureValue) < 5)
        {
            pic_airconditioneStatus.Image = Properties.Resources.switch_on;
            IsairconditioneOpen = true;
            trb_airconditione.Value = 25;
        }
        else
        {
            pic_airconditioneStatus.Image = Properties.Resources.switch_off;
            IsairconditioneOpen = false;
            trb_airconditione.Value = 18;
        }
    }
    else if (____)// 判断com_mode控件选择的"考试模式"
    {
        ____; //将窗帘设置为0全开(trackBar 控件名为trb_curtain)
        if (____)//光照度低于200则打开灯
        {
            pic_lightsStatus.Image = Properties.Resources.switch_on;
            IsLampOpen = true;
            lamp.OnOff(true);//打开界面灯
            DeviceCtrlHelper.adam4150Helper.OnOff(1, true);//打开1通道灯
        }
        else if (____) //光照度大于400
        {
            pic_lightsStatus.Image = Properties.Resources.switch_off;
            IsLampOpen = false;
            lamp.OnOff(false);//关闭界面灯
            DeviceCtrlHelper.adam4150Helper.OnOff(1, false);//关闭1通道灯
        }
    }
}

说明:这道练习隐含要求在窗体上新增下拉框 com_mode(选项「上课模式」「考试模式」)和窗帘滑动条 trb_curtain。另外,提示代码中上课模式的开灯阈值是 <200,任务实施里是 <400;提示代码的上课模式分支漏了 adam4150Helper.OnOff(1,false),书上原样如此。

自主探究(书140):

在「边想边做」的基础上,为程序增加以下功能:若当前环境实时温度低于5℃,则自动关闭窗户(使用 TrackBar 控件模拟)并打开空调,设置为25℃。

任务2 实现智慧寝室(书141–148 / PDF 142–149)

任务描述:寝室有四种情景模式,根据采集的光照、温湿度和定时自动切换:

  • 起床模式:开灯光,开广播放音乐,窗帘拉到半开
  • 上课模式:关灯光和广播,窗帘全开
  • 熄灯模式:关灯光、广播和窗帘
  • 回寝模式:开灯光,关窗帘,开广播放音乐

任务实施 1–4. 与任务1相同,项目名「寝室情景模式」,保存到 D:\物联网编程与应用\项目7\任务2\。没有硬件就用仿真 dll。 5. 添加资源 switch_off.png、switch_on.png、ring.mp3。 6. 按图7-4建窗体(标题「智慧寝室情景控制」):

  • 顶部「模式选择」组:「当前时间」文本框 +「开启情景模式」按钮
  • 左侧「采集量」组:串口、光照度、温度、湿度
  • 右侧「状态栏」组:「窗帘」TrackBar(两端标「全开」「全关」)、「广播」开关图片、「灯光」灯泡

表7-5 窗体界面控件属性:

控件名称 控件类型 属性名 属性值 控件功能
frm_control 窗体 Text 智慧寝室情景控制
grp_mode 组合框 Text 模式选择
lbl_time 标签 Text 当前时间
txt_time 文本框 显示时间
lbl_mode 标签 Text 显示当前情景模式状态
btn_apply 按钮 Text 开启情景模式 控制模式开关
grp_input 组合框 Text 采集量
lbl_import 标签 Text 串口
lbl_light 标签 Text 光照度
lbl_temperation 标签 Text 温度
lbl_humidity 标签 Text 湿度
com_import 下拉列表框 设置采集串口
txt_light 文本框 显示光照度
txt_temperation 文本框 显示温度
txt_humidity 文本框 Text 显示湿度
grp_status 组合框 Text 状态栏
lbl_light 标签 Text 灯光
lbl_curtain 标签 Text 窗帘
lbl_boardcast 标签 Text 广播
lamp 灯泡 界面电灯开关
trb_curtain 滑动条 模拟窗帘开关
lbl_curtainopen 标签 Text 全开
lbl_curtainclose 标签 Text 全关
pic_boardcastStatus 图片框 Image Switch_off.png 显示广播状态
timer 时间控件 Interval 1000 实现实时采集
axWindowsMediaPlayer1 Windows媒体播放器控件 Visible False 用于播放mp3音乐

小贴士(axWindowsMediaPlayer 的添加方法):在工具箱中右键 →「选择项」→「选择工具箱项」→「COM组件」标签,勾选「Windows Media Player」→ 确定,然后从工具箱把控件拖到窗体上。

  1. 引用 System.IO.Ports、DeviceCtrlLibrary、DeviceCtrlLibrary.Model、System.Threading。
  2. 定义变量:bool IsOpen、IsLampOopen(原文拼写)、IsBoardcastOpen;ADAM4017ResModel ResModel;DateTime now;四个时间点 GetUp = DateTime.Today.AddHours(6.75)(6:45)、ClassBegin = AddHours(7.5)(7:30)、ClassEnd = AddHours(21.0)、Sleep = AddHours(22.0)。
  3. Load:清空 com_import,填入串口名。
  4. btn_apply_Click:在「开启情景模式」和「停止情景模式」之间切换,同时开/关 4017、4150 和 timer。
  5. 写 CheckMode() 方法:按时间段 GetUp≤now<ClassBegin / ClassBegin≤now<ClassEnd / ClassEnd≤now<Sleep / 其他,分别对应起床 / 上课 / 回寝 / 熄灯模式。每种模式设置 lbl_mode 文字(「智慧寝室正处于起床模式」等)、灯(4150 通道1 + lamp)、广播(new Thread(PlayMp3).Start() 或把图片切到 off),以及 trb_curtain.Value(起床 5,上课 0,回寝 10,熄灯 10)。
  6. 写 PlayMp3():图片切到 on,执行 axWindowsMediaPlayer1.URL = AppDomain.CurrentDomain.BaseDirectory + "//Resources//ring.mp3"; axWindowsMediaPlayer1.Ctlcontrols.play();,然后 Thread.Sleep(4000),图片切回 off。
  7. timer_Tick:now = DateTime.Now,txt_time.Text = now.ToString("HH:mm:ss"),采集并显示数据,然后调用 CheckMode()。

知识提炼

  1. PictureBox 控件:可以加载 BMP、ICO、WMF、JPEG、GIF。表7-6 常见属性:Image(要显示的图像)、SizeMode(图像显示模式)。
  2. 自定义方法:语法为 <Access Specifier> <Return Type> <Method Name>(Parameter List) { Method Body },书中逐一解释了访问修饰符、返回类型(不返回值时用 void)、方法名(唯一,区分大小写)、参数列表和方法主体。示例:
public int ADD(int x, int y)
{
    Int s;   // 原文就是大写 Int,属于书上的笔误
    s=x+y;
    return s;
}

能力拓展

边想边做(书147):

通过「任务实施」的操作步骤,已经实现了智慧寝室各个模式的基本功能。在此基础上为程序添加报警灯(Alarm)和定时器(timer)控件,定时器时间间隔为 0.5s,报警灯默认情况下 Visible 为 false,请完善 timerAlarm_Tick 事件,使在熄灯模式下温度超过 32℃ 时启动界面报警闪烁功能。

提示1:根据下列代码的提示,完善 timer_Tick 事件代码。

private void timerAlarm_Tick(object sender, EventArgs e)
{
    IsAlarmOpen = ____; // IsAlarmOpen为报警灯状态变量,每次进行切换
    alarm.OnOff(____);//切换界面报警灯状态
}

提示2:根据下列代码的提示,修改完善 CheckMode 方法的代码。

private void CheckMode()//切换各个模式
{
//上述代码省略,具体参照任务实施部分代码
    else
    {
        lbl_mode.Text = "智慧寝室正处于熄灯模式";
        if (IsLampOopen == true)
        {
            IsLampOopen = false;
            DeviceCtrlHelper.adam4150Helper.OnOff(1, false);
            lamp.OnOff(false);
        }
        if (IsBoardcastOpen == true)
        {
            IsBoardcastOpen = false;
            pic_boardcastStatus.Image = Properties.Resources.switch_off;
        }
        trb_curtain.Value = 10;
        if (____)//温度较高开启报警
        {
            alarm.Visible = true;//设置报警灯控件alarm为显示状态
            ____//开启报警灯闪烁定时器timerAlarm启动
        }
        else
        {
            ____//设置报警灯控件alarm为不显示状态
            timerAlarm.Stop();//停止报警灯闪烁定时器timerAlarm
        }
    }
}

这道题要用到教材类库的 Alarm 控件(名为 alarm),它有 OnOff(bool) 方法和 Visible 属性;还要第二个 Timer timerAlarm,Interval=500。

自主探究(书148):

在光照度和温湿度的基础上,实现对气压(airPressure)和空气质量(airQuality)变化的监测,打开关闭的窗户并启动排风系统。

任务3 实现智慧会议室(书148–155 / PDF 149–156)

任务描述:会议室有会议、讨论、展示三种情景。采集光照度、温度、湿度、空气质量等数据,按预设值控制灯光、窗帘、广播。预约时间到达后:

  • 会议模式:开灯光、窗帘、主传声器,关客传声器
  • 讨论模式:开灯光、窗帘、主传声器、客传声器
  • 展示模式:开灯光、主传声器,关窗帘和客传声器

窗帘用滑块条模拟,主、客传声器用控件模拟。

任务实施 1–4. 与前面相同,项目名「会议模式」,保存到 D:\物联网编程与应用\项目7\任务3\,复制 dll 到 ...\会议室情景模式\bin\Debug\。书上项目名和文件夹名不一致,原文如此。 5. 添加资源 switch_off.png、switch_on.png。 6. 按图7-5建窗体(标题「智慧会议室情景模式」):

  • 顶部「模式选择」组:当前时间「19:46:41」、标签「智慧会议室未开启情景模式」、复选框「拍照记录」;一行「[13]时[20]分[00]秒将会有场 [会议▼] [预约] [退出]」
  • 左侧「采集量」组:串口、光照度、温度、湿度、气压、空气质量,以及「摄像头IP地址」文本框 192.168.14.131
  • 右侧「状态栏」组:窗帘 TrackBar(全开/全关)、灯光开关图片 + 灯泡、「传声器」两个开关(主、客)

表7-7 窗体界面控件属性:

控件名称 控件类型 属性名 属性值 控件功能
frm_control 窗体 Text 智慧会议室情景模式
grp_mode 组合框 Text 模式选择
lbl_time 标签 Text 当前时间:00:00:00
lbl_mode 标签 Text 智慧会议室未开启情景模式
chk_record 复选框 Text 拍照记录 设置拍照开关
txt_hour 文本框 Text 13 设置预约时间的小时数
txt_minute 文本框 Text 20 设置预约时间的分钟数
txt_second 文本框 Text 00 设置预约时间的秒数
lbl_hour 标签 Text 时
lbl_minute 标签 Text 分
lbl_second 标签 Text 秒将会有场
com_mode 下拉列表框 Text 会议 选择模式
btn_reserve 按钮 Text 预约 预约模式
btn_exit 按钮 Text 退出 退出模式
grp_input 组合框 Text 采集量
lbl_import 标签 Text 串口
com_import 下拉列表框 设置采集串口
lbl_light 标签 Text 光照度
lbl_temperation 标签 Text 温度
lbl_humidity 标签 Text 湿度
lbl_airPressure 标签 Text 气压
lbl_airQuality 标签 Text 空气质量
lbl_CO2 标签 Text CO₂浓度
txt_light 文本框 显示光照度
txt_temperation 文本框 显示温度
txt_humidity 文本框 Text 显示湿度
txt_airPressure 文本框 Text 显示气压
txt_airQuality 文本框 Text 显示空气质量
txt_CO2 文本框 Text 显示CO₂浓度
lbl_camera 标签 Text 摄像头IP地址
txt_camera 文本框 Text 192.168.14.131 设置摄像头IP地址
grp_status 组合框 Text 状态栏
lbl_light 标签 Text 灯光
lbl_curtain 标签 Text 窗帘
lbl_boardcast 标签 Text 传声器
lamp 灯泡 模拟电灯开关
trb_curtain 滑动条 模拟窗帘开关
lbl_curtainopen 标签 Text 全开
lbl_curtainclose 标签 Text 全关
pic_lightsStatus 图片框 Image Switch_off.png 控制并显示电灯状态
pic_mainMicrophone 图片框 Image Switch_off.png 控制并显示主传声器状态
pic_subMicrophone 图片框 Image Switch_off.png 控制并显示客传声器状态
timer 时间控件 Interval 2000 实现实时采集

注意:图7-5 截图里没有 CO₂ 那一行。

  1. 引用 System.IO、System.IO.Ports、DeviceCtrlLibrary、DeviceCtrlLibrary.Model。
  2. 定义变量:bool IsLightsOpen、IsmainMicrophoneOpen、IssubMicrophoneOpen、IsInitialized;ADAM4017ResModel ResModel;DateTime now;DateTime reserve = DateTime.Now.AddDays(1)(默认设为明天,防止出现「假预约」);Image ReadImage = null。
  3. Load:清空 com_import 和 com_mode,com_mode 加入「会议」「讨论」「展示」,启动 timer。如果 BaseDirectory + "Camera/" 不存在就创建。然后 DeviceCtrlHelper.ipCameraHelper.ImageRead += ipCameraHelper_ImageRead; 绑定监控事件。
  4. void ipCameraHelper_ImageRead(ImageReadEvenArgs e) { ReadImage = e.img; }
  5. btn_reserve_Click:reserve = DateTime.Today.AddHours(时).AddMinutes(分).AddSeconds(秒)(逐行调用 Convert.ToInt16)。 12–14. pic_lightsStatus / pic_mainMicrophone / pic_subMicrophone 的 Click:手动开关切换。
  6. ChooseMode():lbl_mode.Text = "智慧会议室已开启" + com_mode.Text + "模式",然后 switch 三种模式设置灯、窗帘(0 开 / 10 关)、主客传声器图片。
  7. timer_Tick:每次 Tick 都执行 adam4017Helper.Open(com_import.Text) 和 adam4150Helper.Open(...),然后显示时间、采集光照/温度/湿度/气压(airPressureValue)/空气质量(airQualityValue)。当 now >= reserve 时:如果还没初始化,就 ChooseMode();若 chk_record 勾选,则 ipCameraHelper.Open(txt_camera.Text, "admin", "") 加 ipCameraHelper.Start();然后 IsInitialized=true。在 now.Second == 0 && now.Minute % 5 == 0 时,执行 ReadImage.Save(BaseDirectory + "Camera/" + DateTime.Now.ToString("yyyyMMdd_HH mm ss") + ".jpg")。
  8. btn_exit_Click:停止 timer,reserve 设为明天,chk_record.Enabled=false,关灯,trb_curtain.Value=0(注释写的是「关闭窗帘」,但 0 实际是全开),关主客传声器,IsInitialized=false,关闭 4150、4017,ipCameraHelper.Stop()。

知识提炼:+= 在 C# 中有两种用法:一是复合赋值(i+=1 即 i=i+1);二是注册/订阅事件,如 winForm.Closed += new EventHandler(child_Closed);,变量句柄类型为 EventHandler。用 -= 取消订阅。事件发生时,处理程序集合中的所有方法依次调用。

能力拓展

边想边做(书155):

通过「任务实施」的操作步骤,已经实现了智慧会议室的基本配置。现在修改程序:通过增加一个文本框控件 txt_duration 获取预约时长(单位 min),实现预约时间内设备的开启,预约时间结束后则关闭设备。请根据下列提示,实现上述功能。 提示:根据以下提示,完善 timer_Tick 事件

private void timer_Tick(object sender, EventArgs e)
{
//以上代码忽略,详见任务实施timer_Tick事件代码
    if (now >= reserve && ____)//是否在预约时间段内,txt_duration的Text属性接收预约时长
    {
        if (!IsInitialized)
        {
            ChooseMode();//进入选择的模式
            //此部分代码忽略,详见任务实施timer_Tick事件代码
        }
    }
    else//预约时间外关闭设备
    {
        ____//停止timer定时器
        ____//修改reserve变量为明天,防止初始值小于当前时间的假预约情况
        chk_record.Enabled = false;
        pic_lightsStatus.Image = Properties.Resources.switch_off;//转换灯光图片
        lamp.OnOff(false);//关闭界面灯光
        DeviceCtrlHelper.adam4150Helper.OnOff(1, false);//关闭1通道灯
        trb_curtain.Value = 0;//关闭窗帘
        pic_mainMicrophone.Image = Properties.Resources.switch_off;//关闭主传声器
        pic_subMicrophone.Image = Properties.Resources.switch_off;//关闭客传声器
        IsInitialized = false;//初始化状态
        DeviceCtrlHelper.adam4150Helper.Close();
        DeviceCtrlHelper.adam4017Helper.Close();//关闭串口
        DeviceCtrlHelper.ipCameraHelper.Stop();//关闭视频监控
    }
}

(原文印刷错误:lam•p.OnOff(false) 应为 lamp.OnOff(false)。另外这道题的逻辑有缺陷:预约时间到达之前,timer 也会走进 else 分支把 timer 停掉。)

自主探究(书155):

在「边想边做」的基础上,为程序增加下列功能:当开启情景模式时,若时间早于 6 时或晚于 18 时,则窗帘自动关闭,打开照明灯。

项目7 项目评价(书156)

任务 评价指标 具体要求
任务1 窗体界面 窗体控件正确,布局美观
任务1 数据显示 能实时显示环境数据
任务1 自动控制 能根据环境数据的变化实时控制智能设备
任务2 窗体界面 窗体控件正确,布局美观
任务2 数据显示 能实时显示环境数据
任务2 自动控制 能根据时间段的变化判断寝室情景实时控制智能设备
任务3 窗体界面 窗体控件正确,布局美观
任务3 数据显示 能实时显示环境数据
任务3 实现预约时段后开启自动控制功能 能根据预约时间控制智能设备
任务3 监控抓取 能定时实现监控数据的抓取
职业素养 代码书写规范 应该遵从结构化的要求,采用缩进的格式
职业素养 注释书写规范 做到清晰,准确地描述内容。对于程序中复杂的部分必须有注释加以说明。注释量要适中,避免过多或过少导致阅读困难
职业素养 变量命名规范 选择有意义的英文进行命名,使变量名能清楚地表达其含义。尽量少使用人名、地名和汉语拼音。应使用那些约定俗成的缩写。多个单词组成的变量名,每个单词的首字母应该大写

后面是「个人学习总结」(项目学习情况 / 心得与反思)。表格没有分值。

项目7 书与参考代码的差异 / 模拟器要点

  • 任务1 pic_lightsStatus_Click 的判断条件,书上写反了:书上是 if (!IsLampOpen) { 关灯 } else { 开灯 },参考代码 项目7/任务一/任务1/教室情景模式/教室情景模式/教室情景模式.cs 是 if (IsLampOpen) { 关灯 }。按书上写,点了灯不会亮。
  • 任务1参考工程里的窗体文件叫 教室情景模式.cs,不是 Form1.cs。参考 Designer 中 trb_airconditione 的 Minimum=18、Maximum=32、Value=18,书上表格没写这几个值,起始模板要加上。timer.Interval 两边都是 2000。
  • 任务2:PlayMp3 在后台线程里直接改 pic_boardcastStatus.Image 并调用 WMP 控件。书上和参考代码都没有设置 CheckForIllegalCrossThreadCalls=false,也没有用 Invoke(Release 版本能跑,调试时会报跨线程异常)。模拟器要么允许跨线程访问 UI,要么自动封送到 UI 线程。参考 Designer 里多了一个 alarm1(WinFormsDeviceControlLibrary.Alarm,尺寸 7×6),可能是给「边想边做」预留的。
  • 模拟器需要支持的控件和 API(从代码本身看不太出来):
    • WinFormsDeviceControlLibrary.Lamp:OnOff(bool)
    • WinFormsDeviceControlLibrary.Alarm:OnOff(bool)、Visible(任务2拓展用)
    • AxWindowsMediaPlayer(COM 控件):URL 属性、Ctlcontrols.play(),要能播放资源里的 ring.mp3,可以简化为提示音或「广播中」动画
    • Properties.Resources.switch_on/switch_off(png 图片资源)
    • System.Threading.Thread、Thread.Sleep
    • ADAM4017ResModel 的字段:lightValue、temperatureValue、humidityValue、airPressureValue、airQualityValue,都是 string。参考代码注释里还提到 CO2Value,书上表格有 txt_CO2,但代码里从没赋值
    • adam4150Helper.OnOff(通道, bool)
    • ipCameraHelper.Open(ip, uid, pwd)、.Start()、.Stop()、ImageRead 事件(参数类型 ImageReadEvenArgs,字段 .img,类型为 Image)、Image.Save(path);以及 Directory.Exists、Directory.CreateDirectory、AppDomain.CurrentDomain.BaseDirectory
    • 任务2 依赖真实时钟(6:45 / 7:30 / 21:00 / 22:00),任务3 依赖预约时间和「每5分钟整点拍照」。模拟器最好提供可调的模拟时钟,否则学生很难测试各种模式
  • 任务3:书上和参考代码的 Load 都没有往 com_import 填串口名(注释里写了但代码没做),而 timer 从 Load 起就在跑,并且每个 Tick 都 Open 一次串口。参考代码 timer.Interval=1000,书上表格是 2000。两边都没有给 txt_CO2 赋值。ChooseMode 只改了图片,没有同步 IsLightsOpen 等状态变量,所以模式切换后手动点开关时第一次点击状态会对不上(书上和参考代码都这样)。

项目8 智慧校园管理平台开发(书157–186)

项目概述:综合前面7个项目,做一个智慧校园管理平台,对校园物联网设备和信息化做监控和管理,方便管理部门实时监控、处理突发事件,界面要友好。

学习目标:

  • 理解平台各功能模块的用途及模块间的相互关系
  • 掌握各界面设计以及图标绘制的方法
  • 能根据实际应用需求完成物联网应用界面设计
  • 能对传感器采集的数据做简单分析
  • 能识别系统开发中的常见错误与系统故障,掌握基本排查方法
  • 养成严谨细致的工作作风和认真负责的工作态度
  • 培养坚韧不拔、勇于面对困难的品质

本项目各任务都没有「知识提炼」和「能力拓展」,只有任务描述和任务实施,最后是项目评价。

任务1 制作管理平台的主界面(书157–159 / PDF 158–160)

任务描述:主界面提供所有功能的入口,要求简洁、易学、好用。包含两个入口:校园环境、校园安防。

任务实施

  1. 新建 WinForms 项目「智慧校园」,保存到 D:\物联网编程与应用\项目8\任务1\。
  2. 把「综合实训」文件夹下的 Resources 文件夹复制到 ...\智慧校园\bin\Debug,并在解决方案中导入。参考工程对应的是 项目8/Resources。
  3. 设置窗体属性。表8-1 窗体属性:
属性名 属性值
Name Main
FormBorderStyle none
StartPosition CenterScreen
  1. 加两个 Panel:

    • panel1:BackColor = 53,147,237,Dock = Top;里面放一个 PictureBox,SizeMode = StretchImage,Image = Resources/ico_title.png(「智慧校园」标题图)
    • panel2:Dock = Fill,BackgroundImageLayout = Stretch,BackgroundImage = Resources/bg_home.png(校园建筑照片)

    效果见图8-1。

  2. 在 panel2 上放 3 个 PictureBox(效果见图8-2,三个方形图标:校园环境/树、校园安防/盾牌、退出/箭头):

表8-2 校园环境图片框属性:

属性名 属性值
Name pic_environment
BackColor Transparent
Image 智慧校园.Properties.Resources.icon_02

表8-3 校园安防图片框属性:

属性名 属性值
Name pic_security
BackColor Transparent
Image 智慧校园.Properties.Resources.icon_03

表8-4 退出图片框属性:

属性名 属性值
Name pic_exit
BackColor Transparent
Image 智慧校园.Properties.Resources.icon_04
  1. pic_exit 的 Click:this.Close();

任务2 制作「校园环境」界面(书159–164 / PDF 160–165)

任务描述:实时采集温度、湿度等气象信息并推送到 LED 屏;监测火焰、烟雾等安防信息,一旦有险情第一时间推送到 LED 预警;根据光照度自动开关路灯;对历史气象数据做分析比较。

任务实施(本任务只做界面)

  1. 新建窗体 Environment,并调整大小。表8-5 Environment 窗体属性:
属性名 属性值
Name Environment
FormBorderStyle none
StartPosition CenterScreen
  1. 布局 Panel:

    • panel1:BackColor = 100,116,133,Dock = Top,内放 PictureBox(StretchImage,ico_title.png)
    • panel2:Dock = Fill,panel2 里再放 panel3(Dock = Top)

    效果见图8-3。

  2. panel3:BackColor = 229,229,229,上面放标签 lb_return,Text =「<返回主窗体」(图8-4)。

  3. panel3 下方放 panel_bg:Dock = Fill,BackgroundImageLayout = Stretch,BackgroundImage = day_02.png(带路灯的建筑图,图8-5)。

  4. 在 panel_bg 上放 panel5(Dock = Bottom),控件见表8-6,界面见图8-6:一行「环境数据 火焰[ ] 烟雾[ ] 光照度[ ] 温度[ ] 湿度[ ] [数据分析] [推送天气信息]」。

表8-6 窗体控件:

控件类型 属性名 属性值 控件功能
文本框 Name txt_fire 显示火焰值文本框
文本框 Name txt_smoke 显示烟雾值文本框
文本框 Name txt_light 显示光照度值文本框
文本框 Name txt_temperature 显示温度值文本框
文本框 Name txt_humidity 显示湿度值文本框
按钮 Name 数据分析 数据分析按钮
按钮 Name 推送天气信息 推送天气信息按钮
其余均为标签

(书上把按钮的「Name」写成了中文文字。参考代码中的实际名字是 btn_dataAnalyse(Text「数据分析」)和 btn_ledEnvironment1(Text「推送天气信息」)。)

  1. 在 panel_bg 上放 panel_form:BackColor = Transparent,BackgroundImage = bg_form2.png,Dock = Right(图8-7)。参考资源里的文件名是 bg_from2.png,书上写成了 bg_form2。
  2. 在 panel_form 上做两个模块(图8-8):
    • 「路灯控制」组:复选框「自动控制」、「设定开灯光照度值 [ ] lx」、「灯光控制 [开关]」
    • 「LED推送」组:「LED信息」多行文本框、复选框「自动推送火情」、按钮「推送LED信息」「推送天气信息」

表8-7 panel_form 里面主要控件的属性设置:

控件类型 属性名 属性值 控件功能
复选框 Name ch_autoLamp 自动控制复选框
复选框 Text 自动控制
文本框 Name txt_autoLight 设置控制开灯的光照度值
图片框 Name pic_lampOpen 灯光控制图片框
图片框 BackColor Transparent 灯光控制图片框
图片框 BackgroundImage 智慧校园.Properties.Resources.switch_off 灯光控制图片框
复选框 Name ch_autoLedFire 自动推送火情复选框
复选框 Text 自动推送火情
按钮 Name 推送天气信息 推送天气信息按钮
按钮 Name 推送LED信息 推送LED信息按钮
文本框 Name txt_ledMsg LED信息文本框
其余控件 BackColor Transparent

(参考代码中按钮名为 btn_ledEnvironment2(推送天气信息)和 btn_ledSendMsg(推送LED信息)。)

  1. 在 panel_bg 上加 Chart 图表控件 chart1:
    • Legends 加两项:MaxLegend、AVGLegend
    • Series 设置(图8-9、图8-10「Series 集合编辑器」):
Series XValueMember YValueMembers ChartArea ChartType IsVisibleInLegend Legend LegendText
MaxSeries _Time MaxTemperature ChartArea1 Line True MaxLegend 最大值
AVGSeries _Time AVGTemperature ChartArea1 Line True AVGLegend 平均值

另外 XValueType=Auto,YValuesPerPoint=1,YValueType=Auto。完整效果见图8-11:左下方是折线图(Y 轴 0–100,X 轴 1–8,图例「平均值」「最大值」),右侧是控制面板。

任务3 实现「校园环境」数据采集及数据分析功能(书164–174 / PDF 165–175)

任务描述:实现数据库的查询分析,并把结果显示到界面上。

任务实施(代码在参考工程 项目8/智慧校园/智慧校园/ 里都有,这里只列步骤)

  1. 导入数据采集相关 dll;新建 AppSet 类和 SQLHelpers 文件夹,文件夹里放 SQLBLL、SQLHelper 两个类。
  2. AppSet.cs 内容:串口 ADAM4017_COM="COM2"、ADAM4150_COM="COM3"、LED_COM="COM5"、ZigBee_COM="COM4";摄像机 Camera_Ip="192.168.1.100"、Camera_Uid="admin"、Camera_Pwd="";SqlConn="Data Source=.;Initial Catalog=WisdomCampus;User ID=sa;Password=123"。 小贴士:运行前先配置好数据库。在「综合实训」文件夹找到数据库文件 WisdomCampus,附加到数据库中;设置用 sa 用户登录、密码 123;导入程序运行需要的库文件;按串口设置连接好对应串口。 3–4. SQLHelper(引用 System.Data.SqlClient、System.Drawing、System.IO):
    • strConn = AppSet.SqlConn
    • SaveData(sql, DataTable):SqlDataAdapter + SqlCommandBuilder + Update
    • getData(sql, params SqlParameter[]) 返回 DataSet
    • getData(SqlCommand)
    • Exec(sql, params SqlParameter[]):ExecuteNonQuery,异常时返回 −1 5–6. SQLBLL:
    • SecurityUpData(Image):用 MemoryStream 把图片存成 Jpeg 字节,执行 INSERT INTO SecurityData (EvenTime,EvenImage) VALUES (@time, @img)
    • GetSecurityData(DateTime startDate, DateTime endDate) 返回 Dictionary<string, Image>:执行 SELECT * FROM SecurityData WHERE EvenTime between @time_start and @time_end,end 拼成「年/月/日 23:59:59」,然后 Image.FromStream(new MemoryStream((byte[])...))。书上这段有笔误 eturn ImgDictionary;
    • EnvironmentUpData(bool fire, bool smoke, string light, string temperature, string humidity):执行 INSERT INTO EnvironmentData (Fire, Smoke, Temperature, Humidity, Light, _Time) VALUES (@fire,@smoke,@temperature,@humidity,@light,@time)
    • GetEnvironmentGraphData():执行 SELECT TOP 7 CONVERT(varchar(10),_Time,120) AS _Time, ROUND(Max(Temperature),2) AS MaxTemperature, ROUND(AVG(Temperature),2) AS AVGTemperature FROM EnvironmentData GROUP BY CONVERT(varchar(10),_Time,120) ORDER BY CONVERT(varchar(10),_Time,120) DESC
  3. 在 SQLBLL 文件中加 class SQLRes { string Msg; bool Result; }。 8–9. 主窗体引用 DeviceCtrlLibrary。Main_Load 中依次执行 adam4017Helper.Open(AppSet.ADAM4017_COM)、adam4150Helper.Open(AppSet.ADAM4150_COM)、zigBeeHelper.Open(AppSet.ZigBee_COM)、ledHelper.OpenLedCom(AppSet.LED_COM),全部包在 try/catch 里。
  4. pic_environment_Click:new Environment().Show(); 11–12. Environment 引用 DeviceCtrlLibrary、DeviceCtrlLibrary.Model、智慧校园.SQLHelpers、System.Threading;字段有 Thread thread、SynchronizationContext _main、ADAM4017ResModel ResModel_4017、ADAM4150ResModel ResModel_4150、double environmentLight, setLight、bool IsAuto_Lamp, IsAuto_FireMsg, IsLampOpen。
  5. Environment_Load:chart1.Visible=false;thread = new Thread(new ThreadStart(Tick));_main = WindowsFormsSynchronizationContext.Current;thread.Start()。
  6. Tick() 是一个 while(true) 循环:
    • 采集 4017,Thread.Sleep(500),再采集 4150
    • 通过 _main.Send(_ => {...}, null) 回到 UI 线程更新显示:txt_fire = fireValue ? "发生火情" : "正常",txt_smoke = smokeValue ? "发生烟情" : "正常",以及光照、温度、湿度
    • 自动路灯:double.TryParse(lightValue, out environmentLight);当 IsAuto_Lamp 为真且 environmentLight < setLight 时 LampOnOff(1,true),否则 LampOnOff(1,false)
    • 自动推送火情:火且烟时推送「发生严重火灾」,只有火时推送「发现火情」,只有烟时推送「发现烟情」,都用 ledHelper.SendLedString
    • 调用 SQLBLL.EnvironmentUpData(...) 上传数据,然后 Thread.Sleep(5000)
  7. LampOnOff(int RelayNum, bool IsOpen):调用 zigBeeHelper.OnOff(RelayNum, IsOpen),再用 _main.Send 切换背景:开灯时 panel_bg.BackgroundImage = day_01、pic_lampOpen.BackgroundImage = switch_on,关灯时换成 day_02 和 switch_off。
  8. Environment_FormClosed:thread.Suspend(); thread = null;
  9. btn_dataAnalyse_Click:按钮文字为「数据分析」时,chart1.DataSource = SQLBLL.GetEnvironmentGraphData(); chart1.DataBind(); chart1.Visible = true;,并把按钮改为「关闭图表」;为「关闭图表」时反过来。
  10. 两个「推送天气信息」按钮:SendLedString("当前温度:" + temperatureValue + "℃ 湿度:" + humidityValue + "%")。
  11. pic_lampOpen_Click:根据 IsLampOpen 调用 LampOnOff 切换,然后 IsLampOpen = !IsLampOpen。
  12. 「推送LED信息」按钮:SendLedString(txt_ledMsg.Text)。
  13. txt_autoLight_TextChanged:double.TryParse(txt_autoLight.Text, out setLight)。
  14. ch_autoLamp_CheckedChanged:IsAuto_Lamp = ch_autoLamp.Checked;
  15. 书上写「在 ch_autoLamp 的 CheckedChanged 事件中输入」IsAuto_FireMsg = ch_autoLedFire.Checked;。这里书上写错了,应该是 ch_autoLedFire 的 CheckedChanged。

任务4 实现「校园安防」功能模块(书174–180 / PDF 175–181)

任务描述:实时监控,有人入侵时摄像机抓拍,把非法入侵人员拍照记录下来。

任务实施

  1. 添加窗体 Security.cs 和 SecurityCameraData.cs,Security 的顶部按任务2的方法布局(图8-12:标题栏 +「<返回主窗体」)。
  2. 加 panel_bg:Dock = Fill,BackColor = White。
  3. panel_bg 上加 panel_bg_video:BackColor = DimGray。
  4. panel_bg_video 上加 PictureBox pic_video:BackgroundImage = icon_monitor.png,BackgroundImageLayout = Center。
  5. panel_bg_video 上再加 Label「入侵:」和 Label lb_infrared(Text = 「N/A」),效果见图8-13。
  6. panel_bg 上加 panel_video_ctrl:BackgroundImage = panel_video_ctrl.png(圆形方向盘底图),Layout = Stretch。
  7. panel_video_ctrl 上加 4 个 PictureBox:pic_up、pic_down、pic_left、pic_right,BackgroundImage 分别为 btn_arrow_up_normal.png、btn_arrow_down_normal.png、btn_arrow_left_normal.png、btn_arrow_right_normal.png,Layout = Stretch(图8-14)。
  8. panel_bg 上加按钮 btn_camera(Text「拍照」)、btn_history(Text「查看历史图像」),完整效果见图8-15。
  9. 引用 System.IO、System.Threading、DeviceCtrlLibrary、智慧校园.SQLHelpers。
  10. 字段:bool IsInfrared;string ImgDir = AppDomain.CurrentDomain.BaseDirectory + "Camera\\";Thread thread;SynchronizationContext _main;Image img = null。
  11. tick() 是 while(true) 循环:执行 IsInfrared = adam4150Helper.SetData().infraredValue;通过 _main.Send 设置 lb_infrared.Text = IsInfrared ? "发现入侵" : "正常";若有入侵且 img 不为 null,执行 SQLBLL.SecurityUpData(img);然后 Thread.Sleep(1000)。
  12. ipCameraHelper_ImageRead(ImageReadEvenArgs e):img = e.img; pic_video.Image = e.img;(在相机回调线程里直接设置 UI)。
  13. Security_Load:ipCameraHelper.Open(AppSet.Camera_Ip, Camera_Uid, Camera_Pwd),ImageRead += ...,Start();启动线程;ImgDir 不存在时创建。
  14. Security_FormClosed:ipCameraHelper.Stop(); ipCameraHelper.Close(); thread.Suspend(); thread = null;
  15. 「拍照」按钮:pic_video.Image.Save(ImgDir + DateTime.Now.ToString("yyyyMMdd_HHmmss") + ".jpg"); SQLBLL.SecurityUpData(pic_video.Image);
  16. 「查看历史图片」按钮:new SecurityCameraData().ShowDialog(); 17–20. 四个方向图片的 MouseDown / MouseUp:MouseDown 时 BackgroundImage 换成 btn_arrow_xx_press 并调用 ipCameraHelper.PanUp()(或 PanDown / PanLeft / PanRight);MouseUp 时换回 _normal,并再调用一次同一个 Pan 方法。书上和参考代码都这样写,可能这个 API 是开始/停止切换式的。
  17. 主窗体 pic_security 的 Click:new Security().Show();
  18. lb_return 的 Click:this.Close();

任务5 实现「校园安防」模块中显示历史图片的功能(书181–185 / PDF 182–186)

任务描述:从数据库中查出非法入侵时拍下的照片,按时间快速定位目标人物。

任务实施

  1. SecurityCameraData 窗体:StartPosition = CenterScreen,FormBorderStyle = none。
  2. 加 Panel1:BackColor = 100,116,133,Dock = Top。上面放 label1(Text「历史报警记录」,ForeColor = White)和 PictureBox pic_close(BackgroundImage = windowstate_closed_hover.png,Layout = Center),效果见图8-16。
  3. Panel1 下方加 panel_toolBar:BackColor = 229,229,229。上面放两个标签(「开始时间」「结束时间」)、dateTimePicker1、dateTimePicker2,以及按钮 btn_get(Text「查询」),效果见图8-17。
  4. 加 PictureBox pic_camera:BackColor = 64,64,64。上面放标签 lb_noData(Text「NO DATA」,ForeColor = Red,BackColor = 64,64,64,字号 48)和标签 lb_time(Text「TIME」,ForeColor = Red,BackColor = 64,64,64),效果见图8-18。
  5. 在 pic_camera 上加两个 PictureBox pic_up、pic_down:Image 分别为 arrow_left_normal.png、arrow_right_normal.png,SizeMode = CenterImage(图8-19:左右翻页箭头)。
  6. 引用 DeviceCtrlLibrary、智慧校园.SQLHelpers。
  7. 字段:Dictionary<string, Image> ImgDictionary、int index = -1、Image img、List<string> Keys。
  8. ImageChanged(char UpOrDown = '+'):按 '+' 或 '-' 移动 index 并循环取模,然后 TryGetValue(Keys[index], out img),pic_camera.Image = img; lb_time.Text = Keys[index];。
  9. GetData():用 dateTimePicker1/2 的 Value 调用 SQLBLL.GetSecurityData(...),index 置为 -1;没有数据时显示 lb_noData 并 return,否则隐藏 lb_noData,把所有 key 加进 Keys,再调用 ImageChanged()。 注意:Keys 每次查询都没有 Clear,重复查询会越积越多,书上原样如此。
  10. Load:pic_up.Parent = pic_camera; pic_down.Parent = pic_camera; pic_up.Location = new Point(20,0); pic_down.Location = new Point(560,0); GetData();
  11. btn_get 的 Click:GetData();
  12. pic_up 的 Click:ImageChanged('-');
  13. pic_down 的 Click:ImageChanged();
  14. pic_close 的 Click:this.Close();

项目8 项目评价(书185–186)

任务 评价指标 具体要求
任务1 窗体界面 界面布局合理,美观大方,符合使用者的习惯
任务1 功能实现 实现退出功能
任务2 窗体界面 界面布局合理,美观大方,符合使用者的习惯
任务2 功能实现 路灯能手动、自动控制;实现多个LED信息推送功能;图表能正确显示数据
任务3 窗体界面 界面布局合理,美观大方,符合使用者的习惯
任务3 功能实现 人体入侵有文字提示;摄像机能正常打开并可调整方向;摄像机能拍照并保存图片
任务4 窗体界面 界面布局合理,美观大方,符合使用者的习惯
任务4 功能实现 能根据日期查找历史照片并显示;照片能根据图片按钮切换
任务5 窗体界面 界面布局合理,美观大方,符合使用者的习惯
任务5 功能实现 能正确查找时间段内的历史图片并显示
职业素养 界面设计规范 总体原则以用户为中心,有良好的视觉特征,为用户提供直接而直观的入口,界面美观简洁,风格统一
职业素养 代码书写规范 应该遵从结构化的要求,采用缩进的格式
职业素养 注释书写规范 做到清晰、准确地描述内容。对于程序中复杂的部分必须有注释加以说明。注释量要适中,避免过多或过少导致阅读困难
职业素养 变量命名规范 选择有意义的英文进行命名,使变量名能清楚地表达其含义。尽量少使用人名、地名和汉语拼音。应使用那些约定俗成的缩写。多个单词组成的变量名,每个单词的首字母应该大写
职业素养 界面常用控件命名约定 窗体控件命名以控件名称缩写前3个字母开头,以具有代表含义的词来命名,其形式为:控件缩写(前3个字母)+命名(具有代表含义的单词)
职业素养 电工安全操作规范 所有路灯安装操作必须断电,电线接头必须用绝缘包布包扎好,规范使用各类工具

注意:这张评价表的任务编号和正文错开了一位。表中「任务2」包含了正文任务2(界面)和任务3(路灯、LED、图表)的功能;表中「任务3」对应正文的任务4 安防;表中「任务4」「任务5」都在说正文的任务5 历史图片。表格没有分值,后面是「个人学习总结」。

项目8 书与参考代码的差异 / 模拟器要点

  • 参考代码不完整(项目8/智慧校园/智慧校园/Environment.cs):

    • btn_ledSendMsg_Click(书步骤20:推送 txt_ledMsg)是空方法
    • ch_autoLamp_CheckedChanged(书步骤22)是空方法
    • ch_autoLedFire 没有绑定任何 CheckedChanged 处理程序(书步骤23)

    所以参考程序里「自动控制」和「自动推送火情」两个复选框不起作用。做平台时以书为准补全。

  • 书上步骤23的控件名写错了(ch_autoLamp 应为 ch_autoLedFire);表8-6、8-7 把按钮的 Name 写成了中文。实际名字是:btn_dataAnalyse、btn_ledEnvironment1、btn_ledEnvironment2、btn_ledSendMsg。

  • 资源文件名:书上写 bg_form2.png,参考资源中是 bg_from2.png(另有 bg_from1.png)。其他资源都在 项目8/Resources:ico_title、bg_home、icon_02/03/04、day_01/02、switch_on/off、icon_monitor、panel_video_ctrl、btn_arrow_*_normal/press、arrow_left/right_normal、windowstate_closed_hover、pic_big。

  • 数据库:项目8/DataBase/WisdomCampus.mdf。表 EnvironmentData 的字段为 Fire (bit)、Smoke (bit)、Temperature、Humidity、Light、_Time;表 SecurityData 的字段为 EvenTime、EvenImage (varbinary/image)。模拟器的 SQL 层需要支持:

    • T-SQL 函数 CONVERT(varchar(10), _Time, 120)、ROUND、MAX、AVG、GROUP BY、TOP 7
    • 参数化查询 @param(SqlParameter、params SqlParameter[])
    • 二进制图片列(byte[] ↔ Image,经 MemoryStream,Image.Save(ms, ImageFormat.Jpeg)、Image.FromStream)
    • SqlCommandBuilder
    • 若后端是 SQLite,CONVERT 和 TOP 需要做方言转换
  • 新出现的设备 API(之前的项目里没有):

    • DeviceCtrlHelper.zigBeeHelper.Open(com)、.OnOff(relayNum, bool)(路灯继电器)
    • DeviceCtrlHelper.ledHelper.OpenLedCom(com)、.SendLedString(string)(LED 屏,模拟器应显示一块 LED 屏)
    • adam4150Helper.SetData() 返回 ADAM4150ResModel,字段 fireValue、smokeValue、infraredValue 都是 bool
    • ipCameraHelper.Open(ip, uid, pwd) / Start() / Stop() / Close() / PanUp/PanDown/PanLeft/PanRight() / ImageRead 事件
    • 设备串口固定在 AppSet:4017=COM2、4150=COM3、ZigBee=COM4、LED=COM5,摄像机 IP 为 192.168.1.100,用户 admin,密码为空
  • 新出现的 WinForms 特性:

    • 多窗体:new Environment().Show()、new SecurityCameraData().ShowDialog()、this.Close()
    • 无边框窗体 FormBorderStyle=None,自己做标题栏
    • Panel 的 Dock(Top/Fill/Bottom/Right)嵌套布局
    • BackgroundImage + BackgroundImageLayout(Stretch/Center)
    • BackColor=Transparent、RGB 颜色值、Control.Parent 动态改父容器、Location = new Point(...)
    • PictureBox 的 MouseDown/MouseUp 事件,SizeMode 为 StretchImage/CenterImage
    • System.Windows.Forms.DataVisualization.Charting.Chart:Legends、Series、XValueMember/YValueMembers、DataSource + DataBind、ChartType=Line
    • 后台 Thread + SynchronizationContext(WindowsFormsSynchronizationContext.Current、_main.Send(_ => {...}, null))
    • thread.Suspend()(已过时的 API,书上和参考代码都在用,模拟器要么支持,要么提示改用标志位退出)
    • Dictionary<string, Image>、List<string>
    • DateTimePicker.Value
    • double.TryParse(..., out ...)
    • 文本框的 TextChanged 和复选框的 CheckedChanged 事件
  • 任务3 的 Tick 循环:每 5.5 秒采集一次,每次都写入数据库(EnvironmentUpData);图表取最近 7 天的每日最高温和平均温。模拟器要能生成足够的历史数据,图表才有内容。