Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
65a1b3a81e | ||
|
|
b20db5721e | ||
|
|
10c50265b2 | ||
|
|
5ae07b851d | ||
|
|
2cd0111967 | ||
|
|
d60868d912 | ||
|
|
42e258ddcd | ||
|
|
13c3e4c3ec | ||
|
|
19d6872d80 | ||
|
|
eedf1aa2fb |
No files matched your search
@@ -67,8 +67,8 @@ Docker Compose,HTTPS 由服务器上的 Nginx Proxy Manager 负责。推送到
|
||||
已完成:
|
||||
|
||||
- **虚拟实训台**:按教材实训台布局图排列(红外对射、警示灯、LED 屏、照明灯、人体红外、温湿度、光照、烟雾、火焰、ADAM 模块)。每个浏览器会话一台,状态由服务器统一保存:传感器读数由网页上的滑块和开关设定,经服务器推给学生程序;灯、继电器、LED 屏由学生程序控制。
|
||||
- 新控件:CheckBox、RadioButton(事件顺序与 WinForms 一致)、ListBox、PictureBox,教材控件库的 Lamp、Alarm。
|
||||
- 示例工程(按书上代码整理):项目 1 任务 1–2、项目 2 任务 1–3、项目 3 任务 1–2、项目 4 任务 1–3、项目 5 任务 1–3、项目 6 任务 2–3,共 15 个(项目 6 任务 1 在数据库页面上完成)。网址带 `?sample=示例编号` 可直接打开指定示例。
|
||||
- 新控件:CheckBox、RadioButton(事件顺序与 WinForms 一致)、ListBox、PictureBox、DataGridView、DateTimePicker、TrackBar、Windows Media Player、Chart(图表),教材控件库的 Lamp、Alarm;Panel 停靠布局、背景图片、无边框窗体。
|
||||
- 示例工程(按书上代码整理):项目 1 任务 1–2、项目 2 任务 1–3、项目 3 任务 1–2、项目 4 任务 1–3、项目 5 任务 1–3、项目 6 任务 2–3、项目 7 任务 1–3、项目 8 任务 1–5,共 23 个(项目 6 任务 1 在数据库页面上完成)。网址带 `?sample=示例编号` 可直接打开指定示例。
|
||||
- 代码编辑器(Monaco):C# 高亮、编译错误波浪线、点错误跳到对应行、F5 运行 / Shift+F5 停止、草稿自动保存在浏览器本地。
|
||||
- 窗体设计器(仿 VS,界面中文):工具箱、拖放与改大小、属性窗口、事件窗口、双击生成事件处理方法、撤销 / 重做;读写 VS 格式的 Designer.cs。
|
||||
- 多窗体:新建、删除窗体;运行时多个窗口可切换前后。
|
||||
@@ -78,4 +78,4 @@ Docker Compose,HTTPS 由服务器上的 Nginx Proxy Manager 负责。推送到
|
||||
|
||||
尚未完成:
|
||||
|
||||
- 教材项目 7–8(需要 TrackBar、资源图片、图表、虚拟时钟、无边框窗体等)。
|
||||
- 教材项目 8(需要图表、无边框窗体、Panel 停靠、背景图片等)。
|
||||
@@ -23,6 +23,7 @@
|
||||
"name": "@iot/web",
|
||||
"dependencies": {
|
||||
"@iot/shared": "workspace:*",
|
||||
"chart.js": "^4.6.1",
|
||||
"modern-screenshot": "^4.7.0",
|
||||
"monaco-editor": "^0.57.0",
|
||||
"naive-ui": "^2.41.0",
|
||||
@@ -115,6 +116,8 @@
|
||||
|
||||
"@juggle/resize-observer": ["@juggle/resize-observer@3.4.0", "https://registry.npmjs.com/@juggle/resize-observer/-/resize-observer-3.4.0.tgz", {}, "sha512-dfLbk+PwWvFzSxwk3n5ySL0hfBog779o8h68wK/7/APo/7cgyWp5jcXockbxdk5kFRkbeXWm4Fbi9FrdN381sA=="],
|
||||
|
||||
"@kurkle/color": ["@kurkle/color@0.3.4", "", {}, "sha512-M5UknZPHRu3DEDWoipU6sE8PdkZ6Z/S+v4dD+Ke8IaNlpdSQah50lz1KtcFBa2vsdOnwbbnxJwVM4wty6udA5w=="],
|
||||
|
||||
"@napi-rs/lzma-linux-x64-gnu": ["@napi-rs/lzma-linux-x64-gnu@1.5.1", "https://registry.npmjs.com/@napi-rs/lzma-linux-x64-gnu/-/lzma-linux-x64-gnu-1.5.1.tgz", { "os": "linux", "cpu": "x64" }, "sha512-oTXEIha4SsuXdTA4Iyskj0kpdx2yVXdhd75c2v3xGrHFfVMsbhTPZU/nMPL4sWKo4pBHm3aucLaqGlF696dTyQ=="],
|
||||
|
||||
"@rolldown/pluginutils": ["@rolldown/pluginutils@1.0.1", "https://registry.npmjs.com/@rolldown/pluginutils/-/pluginutils-1.0.1.tgz", {}, "sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw=="],
|
||||
@@ -217,6 +220,8 @@
|
||||
|
||||
"bun-types": ["bun-types@1.4.3", "https://registry.npmjs.com/bun-types/-/bun-types-1.4.3.tgz", { "dependencies": { "@types/node": "*", "undici-types": "*" } }, "sha512-tinRJNGOmUN+tEAsn2y2e942n9JHRHR42iUYzDH75NmhJqVBokGSQkkfSFdNWwsANIdrK6ACH9t01M452YyTwg=="],
|
||||
|
||||
"chart.js": ["chart.js@4.6.1", "", { "dependencies": { "@kurkle/color": "^0.3.0" } }, "sha512-hJyniucsFmq//EvXYvJ6c6mahPoI3kaxdjVH8awTaJ/fDNWeSqqqArixMJWkWNN6fcYvPU3HneSANFNYtRi61g=="],
|
||||
|
||||
"css-render": ["css-render@0.15.14", "https://registry.npmjs.com/css-render/-/css-render-0.15.14.tgz", { "dependencies": { "@emotion/hash": "~0.8.0", "csstype": "~3.0.5" } }, "sha512-9nF4PdUle+5ta4W5SyZdLCCmFd37uVimSjg1evcTqKJCyvCEEj12WKzOSBNak6r4im4J4iYXKH1OWpUV5LBYFg=="],
|
||||
|
||||
"csstype": ["csstype@3.2.3", "https://registry.npmjs.com/csstype/-/csstype-3.2.3.tgz", {}, "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ=="],
|
||||
@@ -251,7 +256,7 @@
|
||||
|
||||
"marked": ["marked@14.0.0", "https://registry.npmjs.com/marked/-/marked-14.0.0.tgz", { "bin": { "marked": "bin/marked.js" } }, "sha512-uIj4+faQ+MgHgwUW1l2PsPglZLOLOT1uErt06dAPtx2kjteLAkbsd/0FiYg/MGS+i7ZKLb7w2WClxHkzOOuryQ=="],
|
||||
|
||||
"modern-screenshot": ["modern-screenshot@4.7.0", "", {}, "sha512-9YxN+ddPSMMlhylOv25VHzXrl9u67QRxoh7+SEewGtgUw7t6hHTrjptSDJUSne9oG4Xk/h2cwG15nIt4Hc9ujg=="],
|
||||
"modern-screenshot": ["modern-screenshot@4.7.0", "https://registry.npmjs.com/modern-screenshot/-/modern-screenshot-4.7.0.tgz", {}, "sha512-9YxN+ddPSMMlhylOv25VHzXrl9u67QRxoh7+SEewGtgUw7t6hHTrjptSDJUSne9oG4Xk/h2cwG15nIt4Hc9ujg=="],
|
||||
|
||||
"monaco-editor": ["monaco-editor@0.57.0", "https://registry.npmjs.com/monaco-editor/-/monaco-editor-0.57.0.tgz", { "dependencies": { "dompurify": "3.4.15", "marked": "14.0.0" } }, "sha512-5BkI9KGoqrNvBGUe15/QlZq3OooZ8WLg1AxTpaqHRCP3HNpzPPZKE2EDz8M7c+VRmCeUw1Brp4cx/PWm3kI/5A=="],
|
||||
|
||||
|
||||
@@ -214,8 +214,8 @@ IOT/
|
||||
| 4 入侵监测 | 红外对射检测;网络摄像机画面与云台;报警灯 | PictureBox、摄像头仿真、Alarm 控件、图片保存 | 已完成 |
|
||||
| 5 灾情监测 | 火焰(Timer)、烟雾(Thread)、灾情记录与推送 | Thread(含 Suspend / Resume / Abort)、跨线程改界面、ListBox、FormClosing | 已完成 |
|
||||
| 6 数据存储 | 建库建表、采集入库与增删改、按时间段查询 | SQL 窗口(代替 SSMS)、SqlClient 仿冒(SQLite)、DataGridView、DateTimePicker | 已完成 |
|
||||
| 7 情景控制 | 智慧教室、寝室、会议室三种情景联动 | TrackBar、资源图片、PictureBox 当按钮、mp3 播放、虚拟时钟 | |
|
||||
| 8 管理平台 | 主界面、校园环境(自动控制、7 天温度图表)、校园安防(抓拍入库、历史图片) | Panel Dock、无边框窗体、透明背景、Chart、ShowDialog、图片存数据库 | |
|
||||
| 7 情景控制 | 智慧教室、寝室、会议室三种情景联动 | TrackBar、资源图片、PictureBox 当按钮、mp3 播放、虚拟时钟 | 已完成 |
|
||||
| 8 管理平台 | 主界面、校园环境(自动控制、7 天温度图表)、校园安防(抓拍入库、历史图片) | Panel Dock、无边框窗体、透明背景、Chart、ShowDialog、图片存数据库 | 已完成 |
|
||||
|
||||
每个实验以数据形式存放(目前在 `samples/`,以后迁到 `content/`),由以下部分组成:
|
||||
|
||||
@@ -237,7 +237,7 @@ flowchart TB
|
||||
P0["阶段 0 · 技术验证(已完成 2026-10-22)<br/>教材项目 2 任务 1 原样运行;实测编译、延迟、内存"]
|
||||
P1["阶段 1 · MVP(进行中,约 3 个月)<br/>虚拟实训台 ✔、代码编辑器 ✔、窗体设计器 ✔、多窗体 ✔<br/>教材示例:项目 1–3 全部 ✔,项目 4–5 中平台已支持的 3 个任务 ✔<br/>部署(沙箱、Docker Compose、GitHub Actions)✔"]
|
||||
P2["阶段 2 · 教学闭环(进行中,约 2 个月)<br/>账号与班级 ✔、作品保存在服务器 ✔、自动评分 ✔、布置与提交作业 ✔<br/>评阅(看代码、运行、老师评分、评语)✔、成绩表与导出 ✔;一个班试用"]
|
||||
P3["阶段 3 · 扩展(进行中)<br/>教材项目 4–8:摄像头 ✔、线程 ✔、SQLite 模拟数据库 ✔、图表、虚拟时钟"]
|
||||
P3["阶段 3 · 扩展(进行中)<br/>教材项目 4–8:摄像头 ✔、线程 ✔、SQLite 模拟数据库 ✔、虚拟时钟 ✔、图表 ✔"]
|
||||
P4["阶段 4 · 进阶(之后)<br/>校园街景场景、故障注入题库、导出 VS 工程"]
|
||||
P0 -->|"关口:链路跑通,延迟和资源占用可接受"| P1
|
||||
P1 -->|"关口:教材项目 1–3 能在平台上完整做完"| P2
|
||||
@@ -291,6 +291,26 @@ flowchart TB
|
||||
- **评分**:每个实验可以带 `database.sql`,评分时每个检查项用一份新建的实验库,不碰学生自己的数据;评分规则加"点 / 修改表格单元格""在日期时间选择器里选日期""表格里有 / 没有某行"。新增示例项目 6 任务 2、3(任务 1 在数据库页面上完成),共 15 个。
|
||||
- 实测:用到数据库的学生程序比不用的多约 11 MB 内存(PSS 17 → 28 MB)。另外修正了平台上的时间格式:Linux 的中文区域数据是 09:05:03,改成和中文 Windows 一样的 9:05:03。
|
||||
|
||||
- 2026-10-23:项目 7 情景控制(3 个任务),共 18 个示例。
|
||||
- **虚拟时钟**:实训台加时钟面板,默认真实时间,可以拨到任意日期时刻、选 1×/10×/60× 倍速、一键恢复真实时间;时钟状态由服务器保存(`BoardState.clock`),运行器按它算当前时间。编译时把学生代码里的 `DateTime.Now / Today / UtcNow` 改写成 `SimClock`(和线程 API 的改写一样不换行、编辑器里看到的是原代码)。任务 2 按 6:45 起床、7:30 上课、21:00 回寝、22:00 熄灯切换,任务 3 按预约时刻开启,都靠它来测试。评分规则加 `clock` 步骤(拨到今天的某个时刻,可设倍速)。
|
||||
- **项目资源**(`Properties.Resources`):只用平台素材库(自绘的开关图片、ffmpeg 合成的门铃声,名字和教材一致,`runtime/Sim.WinForms/Resources/gallery`)。实验页"项目资源"对话框勾选素材(图片看缩略图、声音可试听),平台生成 `Properties/Resources.Designer.cs`(图片是 Bitmap、声音是 byte[],用法和 VS 的强类型资源类一样,不在标签页里显示);设计器里图片框的"图片"属性可以从项目资源里选,写进 Designer.cs 的是 `global::命名空间.Properties.Resources.名字`,预览直接显示素材。学生程序的程序目录下有 Resources 文件夹指向素材库(书上用 `BaseDirectory + "//Resources//ring.mp3"`)。没添加资源就用时给中文提示。
|
||||
- **Windows Media Player**(`AxWMPLib.AxWindowsMediaPlayer`):设置 URL 后和真的一样自动播放,`Ctlcontrols.play() / stop()`;声音在浏览器里播放,实训台上加"广播"喇叭,播放时亮起。VS 为它生成的 `ComponentResourceManager(...).GetObject("…OcxState")`:编译时给每个窗体嵌一份空资源,取到 null 不报错。图片、声音数据统一按内容哈希缓存(`blob` 消息),每份只传一次。
|
||||
- **TrackBar**:拖动、点轨道(LargeChange)、方向键(SmallChange),先 ValueChanged 再 Scroll,竖向时最小值在下面;设计器工具箱加"滑动条""媒体播放器"。评分规则加 `value`(滑动条的值)、`media`(播放了某个文件)检查。
|
||||
- 示例:书上任务 1 手动开关灯的判断写反(参考工程是对的),任务 3 漏了填充串口列表,示例里都已改正;任务 3 的"每 5 分钟整点拍照"依赖定时器正好在 0 秒触发,自动评分不检查这一项,由老师评阅。编译检查修正:`System.AppDomain.CurrentDomain.BaseDirectory`(带命名空间)也放行。
|
||||
|
||||
- 2026-10-23:项目 8 智慧校园管理平台(5 个任务,每个任务一个示例,代码逐个累加),至此教材 8 个项目全部完成,共 23 个示例。
|
||||
- **新建类**:实验页"+ 新建类",可以带文件夹(`SQLHelpers/SQLBLL`),文件夹成为命名空间的一段,模板和 VS 2012 一样;可以删除自己建的类。
|
||||
- **停靠布局**:Dock(上、下、左、右、填充)和 WinForms 一样按 Controls 倒序依次停靠,Panel 的边框、GroupBox 的标题占的地方扣掉;窗体改大小、控件显示隐藏、加入移出时重新布局。设计器里改了停靠或大小也立即按同样的规则排好。
|
||||
- **背景图片**:BackgroundImage + BackgroundImageLayout(平铺、居中、拉伸、缩放),窗体、面板、图片框都可以设,设计器里从项目资源选。**背景色沿用父控件**:没设置背景色的标签、面板、分组框等画父控件的背景色(不是父控件的背景图片),和 WinForms 一样,所以书上要把放在背景图上的控件设成 Transparent。分组框的标题处边框断开、透出背景(书上分组框放在深色背景上、标题是白字)。文本框的 BackColor 生效。
|
||||
- **无边框窗体**:FormBorderStyle = None 时没有标题栏;**运行区自动缩放**:窗体比运行区大时(项目 8 的窗体有 1043×791)整体按比例缩小,右上角显示缩放比例;"放大"让运行区占满整个网页(窗体在左、实训台在右),Esc 还原。
|
||||
- **图表**(`System.Windows.Forms.DataVisualization.Charting.Chart`):ChartAreas、Legends、Series(AddXY、Points、ChartType 等)、DataSource + DataBind(DataTable / DataView / DataSet,按 XValueMember、YValueMembers 取列),浏览器用 Chart.js 画,配色是 WinForms 默认的 BrightPastel。设计器工具箱加"图表"(和 VS 一样带 ChartArea1、Legend1、Series1),属性窗口"系列和图例"打开对话框编辑系列(名称、图表类型、图例、图例文字、X / Y 值字段)和图例,按 VS 的格式和顺序写回 Designer.cs,对话框不管的属性原样保留。
|
||||
- **路径的反斜杠**:书上写 `BaseDirectory + "Camera\\"`,Linux 上反斜杠会成为文件名的一部分。编译时把传给 System.IO(File、Directory、Path、各种 Stream)和 Image / Bitmap 读写文件方法的路径参数包上 `WindowsPaths.Fix`,反斜杠换成斜杠(和线程、时钟的改写一样不换行、编辑器里看到的是原代码)。
|
||||
- **其他**:Paint 事件(VS 双击面板生成的空 `panel1_Paint` 能编译,平台不触发);图片框把鼠标按下、松开发给程序(书上方向键按下时转动摄像机、换成按下的图片);数据库里参数化的日期字符串比较时也转换格式(书上拼出的 `"2024/1/23 23:59:59"` 有两个空格)。
|
||||
- **素材**:项目 8 用到的 20 多张图片(标题、校园背景、三个入口图标、亮灯 / 灭灯的路灯场景、面板背景、监控图标、方向盘和方向键、翻页箭头、关闭按钮)全部自绘,名字和尺寸与教材一致(`runtime/Sim.WinForms/Resources/project8.py` 生成 SVG,`build-gallery.sh` 渲染成 PNG)。
|
||||
- **数据库 WisdomCampus**:平台按教材的表结构建(EnvironmentData、SecurityData、Users),预置 7 天环境数据,"数据分析"的图表一打开就有内容。学生第一次运行带预置数据库的实验时自动建好自己还没有的库(已有的库不动)。
|
||||
- **评分规则**新增:`visible`(连同所在容器都可见)、`window`(某个窗体打开着 / 已关闭)、`exited`(程序已退出)、`image` / `background` 写素材名(如 `background: day_01`,按图片内容核对显示的是不是这张素材)、`points`(图表每个系列的数据点数);"点击"图片框时和浏览器一样先发鼠标按下、松开。
|
||||
- **示例**:书上任务 3 第 23 步把"自动推送火情"的处理写到了 ch_autoLamp 上,参考工程里"推送LED信息""自动控制"是空方法、"自动推送火情"没有绑定事件,示例按书的意思补全;任务 5 每次查询前清空 Keys(书上没清空,重复查询时照片和时间对不上)。任务 2 只做界面,为了能看效果加上了打开、返回窗体。
|
||||
|
||||
**其他改进**:
|
||||
|
||||
- 2026-10-22:代码编辑器的语言功能:代码补全(输入 `btn_Open.` 列出按钮的属性、方法、事件,只列出平台仿真了的成员)、悬停提示、参数提示(`MessageBox.Show(` 列出重载,标出正在填的参数)、停止输入 1 秒后自动检查编译错误(提示和点"运行"时一样)。语言服务是一个常驻进程,每个浏览器标签页一份工程,只更新有变化的文件。实测(限制 2 核):50 个标签页同时检查 1.2 秒内全部完成,同时补全 0.7 秒;内存约 260 MB。
|
||||
@@ -308,7 +328,7 @@ flowchart TB
|
||||
| T-SQL 转 SQLite | 教材用 SQL Server 写法(`TOP`、`CONVERT`、DataAdapter 回写、日期字符串) | 已实现:ScriptDom 解析后按白名单转写;连接串只按库名打开学生自己的库;日期统一存为标准格式;STRICT 表和 CHECK 约束让类型、长度错误和 SQL Server 一样报出 |
|
||||
| 旧版线程 API | `Thread.Suspend` / `Resume` / `Abort` 在新版 .NET 上会报错 | 已实现:编译时改写成平台的协作式实现,被挂起或终止的线程在 Sleep、改控件、调设备类库时停下 |
|
||||
| 图片处理 | System.Drawing 在 Linux 上不受支持 | 已实现:自己的 `Image` / `Bitmap` 只保存图片文件数据和尺寸,能显示、保存、读回,不能逐像素绘制 |
|
||||
| 时间相关逻辑 | 项目 3 起就有按时刻开关灯的练习,部分定时器间隔长达 2 小时 | 提供可调、可加速的虚拟时钟 |
|
||||
| 时间相关逻辑 | 项目 3 起就有按时刻开关灯的练习,部分定时器间隔长达 2 小时 | 已实现:实训台上的虚拟时钟可拨时间、1×/10×/60× 倍速,编译时把 DateTime.Now / Today / UtcNow 改写成虚拟时钟 |
|
||||
| 事件延迟 | 浏览器和服务器之间来回传递事件,点击是否有卡顿感 | 已实测:单次约 15 ms,100 个程序同时操作中位数约 60 ms |
|
||||
| 并发资源 | 100 个程序同时运行时的内存和 CPU | 已压测:100 个程序合计约 1.1 GB;2 核下 100 人同时启动约 8 秒;CPU 时间上限防死循环,运行超时回收 |
|
||||
| 公网安全 | 恶意或无意的危险代码 | bubblewrap 沙箱 + 编译检查 + 账号登录(已完成) |
|
||||
|
||||
@@ -44,6 +44,7 @@ internal static class Functions
|
||||
c.CreateFunction<object?, object?>("tsql_ceiling", x => x is null ? null : Math.Ceiling(ToDouble(x)), isDeterministic: true);
|
||||
c.CreateFunction<object?, object?, object?>("tsql_power", (x, y) => x is null || y is null ? null : Math.Pow(ToDouble(x), ToDouble(y)), isDeterministic: true);
|
||||
c.CreateFunction<object?, object?>("tsql_sqrt", x => x is null ? null : Math.Sqrt(ToDouble(x)), isDeterministic: true);
|
||||
c.CreateFunction<object?, object?>("tsql_cmp", v => v is string s && Values.LooksLikeDate(s) ? Values.NormalizeDateLiteral(s) : v, isDeterministic: true);
|
||||
c.CreateFunction("tsql_newid", () => Guid.NewGuid().ToString().ToUpperInvariant());
|
||||
c.CreateFunction("tsql_rand", () => Random.Shared.NextDouble());
|
||||
}
|
||||
|
||||
@@ -911,6 +911,14 @@ internal sealed class Translator
|
||||
_sql.Append(Values.Quote(Values.NormalizeDateLiteral(s.Value)));
|
||||
return;
|
||||
}
|
||||
// 参数的值执行时才知道:是日期样子的字符串时同样转成标准格式(教材 BETWEEN @time_start AND @time_end 传的是字符串)
|
||||
if (expression is VariableReference)
|
||||
{
|
||||
_sql.Append("tsql_cmp(");
|
||||
Expression(expression);
|
||||
_sql.Append(')');
|
||||
return;
|
||||
}
|
||||
Expression(expression);
|
||||
}
|
||||
}
|
||||
@@ -17,13 +17,16 @@ internal static class Values
|
||||
};
|
||||
|
||||
/// <summary>形如日期的字符串字面量(比较时转成标准格式,例如 BETWEEN '2024/1/16 9:05:03' AND ...)</summary>
|
||||
private static readonly Regex DateLike = new(@"^\d{4}[-/.]\d{1,2}[-/.]\d{1,2}([ T]\d{1,2}:\d{1,2}(:\d{1,2}(\.\d+)?)?)?$");
|
||||
private static readonly Regex DateLike = new(@"^\d{4}[-/.]\d{1,2}[-/.]\d{1,2}((\s+|T)\d{1,2}:\d{1,2}(:\d{1,2}(\.\d+)?)?)?$");
|
||||
|
||||
/// <summary>日期和时间之间多余的空格(教材拼出的 "2024/1/23 23:59:59")</summary>
|
||||
private static readonly Regex Spaces = new(@"\s+");
|
||||
|
||||
public static bool LooksLikeDate(string text) => DateLike.IsMatch(text.Trim());
|
||||
|
||||
/// <summary>按 SQL Server 接受的写法解析日期(包括中文 Windows 上 DateTime.ToString() 的 2024/1/16 9:05:03)</summary>
|
||||
public static bool TryParseDateTime(string text, out DateTime value) =>
|
||||
DateTime.TryParseExact(text.Trim(), DateFormats, CultureInfo.InvariantCulture, DateTimeStyles.AllowWhiteSpaces, out value);
|
||||
DateTime.TryParseExact(Spaces.Replace(text.Trim(), " "), DateFormats, CultureInfo.InvariantCulture, DateTimeStyles.AllowWhiteSpaces, out value);
|
||||
|
||||
public static string FormatDateTime(DateTime value) =>
|
||||
value.Millisecond == 0 && value.Ticks % TimeSpan.TicksPerMillisecond == 0
|
||||
|
||||
@@ -22,9 +22,10 @@ public static class TrainingBoard
|
||||
if (!string.IsNullOrEmpty(initial) && JsonNode.Parse(initial) is JsonObject json) Update(json);
|
||||
}
|
||||
|
||||
/// <summary>平台内部使用:应用服务器推送的 {"analog":[…],"digital":[…]}</summary>
|
||||
/// <summary>平台内部使用:应用服务器推送的 {"analog":[…],"digital":[…],"clock":…}(clock 不带时表示不变)</summary>
|
||||
public static void Update(JsonObject json)
|
||||
{
|
||||
if (json.ContainsKey("clock")) Sim.Host.SimClock.Update(json["clock"]);
|
||||
lock (Lock)
|
||||
{
|
||||
if (json["analog"] is JsonArray analog)
|
||||
|
||||
@@ -64,7 +64,9 @@ internal static class ApiPolicy
|
||||
if (name.Parent is QualifiedNameSyntax q && q.Left == name) continue;
|
||||
// var 推断出的类型在初始化表达式里已经检查过
|
||||
if (name is IdentifierNameSyntax { IsVar: true }) continue;
|
||||
if (name.Parent is MemberAccessExpressionSyntax m && m.Expression == name && model.GetSymbolInfo(name).Symbol is INamespaceOrTypeSymbol) continue;
|
||||
// 类型名只是用来限定成员(AppDomain.CurrentDomain、System.AppDomain.CurrentDomain):检查后面的成员就够了
|
||||
ExpressionSyntax qualifier = name.Parent is MemberAccessExpressionSyntax inner && inner.Name == name ? inner : name;
|
||||
if (qualifier.Parent is MemberAccessExpressionSyntax m && m.Expression == qualifier && model.GetSymbolInfo(name).Symbol is INamespaceOrTypeSymbol) continue;
|
||||
|
||||
var symbol = model.GetSymbolInfo(name).Symbol;
|
||||
if (symbol == null) continue;
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
using System.Text;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.CSharp;
|
||||
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||
using Microsoft.CodeAnalysis.Text;
|
||||
|
||||
namespace Sim.Runner;
|
||||
|
||||
/// <summary>
|
||||
/// 编译前把学生代码里的 DateTime.Now / Today / UtcNow 改写成虚拟时钟 Sim.Host.SimClock(实训台上可以拨时间、调倍速,
|
||||
/// 教材项目 7 按 6:45 起床、22:00 熄灯等时刻切换情景模式)。只改写语义上确实是 System.DateTime 这三个属性的地方;
|
||||
/// 改写不换行,报错和异常里的行号不变。学生在编辑器里看到的仍是原来的代码。
|
||||
/// </summary>
|
||||
internal static class ClockRewriter
|
||||
{
|
||||
private const string Target = "global::Sim.Host.SimClock";
|
||||
|
||||
public static CSharpCompilation Rewrite(CSharpCompilation compilation)
|
||||
{
|
||||
var result = compilation;
|
||||
foreach (var tree in compilation.SyntaxTrees)
|
||||
{
|
||||
var text = tree.GetText().ToString();
|
||||
if (!text.Contains("Now") && !text.Contains("Today")) continue;
|
||||
var rewriter = new Rewriter(compilation.GetSemanticModel(tree));
|
||||
var root = rewriter.Visit(tree.GetRoot());
|
||||
if (!rewriter.Changed) continue;
|
||||
result = result.ReplaceSyntaxTree(tree,
|
||||
CSharpSyntaxTree.ParseText(SourceText.From(root.ToFullString(), Encoding.UTF8), (CSharpParseOptions)tree.Options, tree.FilePath));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private static string? Replacement(ISymbol? symbol) =>
|
||||
symbol is IPropertySymbol { IsStatic: true, Name: "Now" or "Today" or "UtcNow" } property
|
||||
&& property.ContainingType.SpecialType == SpecialType.System_DateTime
|
||||
? $"{Target}.{property.Name}"
|
||||
: null;
|
||||
|
||||
private sealed class Rewriter(SemanticModel model) : CSharpSyntaxRewriter
|
||||
{
|
||||
public bool Changed { get; private set; }
|
||||
|
||||
public override SyntaxNode? VisitMemberAccessExpression(MemberAccessExpressionSyntax node)
|
||||
{
|
||||
// DateTime.Now、System.DateTime.Now
|
||||
if (Replacement(model.GetSymbolInfo(node).Symbol) is { } replacement)
|
||||
{
|
||||
Changed = true;
|
||||
return SyntaxFactory.ParseExpression(replacement).WithTriviaFrom(node);
|
||||
}
|
||||
return base.VisitMemberAccessExpression(node);
|
||||
}
|
||||
|
||||
public override SyntaxNode? VisitIdentifierName(IdentifierNameSyntax node)
|
||||
{
|
||||
// using static System.DateTime; 之后直接写 Now
|
||||
if (node.Parent is not MemberAccessExpressionSyntax access || access.Expression == node)
|
||||
{
|
||||
if (Replacement(model.GetSymbolInfo(node).Symbol) is { } replacement)
|
||||
{
|
||||
Changed = true;
|
||||
return SyntaxFactory.ParseExpression(replacement).WithTriviaFrom(node);
|
||||
}
|
||||
}
|
||||
return base.VisitIdentifierName(node);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -17,6 +17,8 @@ internal static class CompileHints
|
||||
("CS0103", "Catch") or ("CS0246", "Catch") => "catch 要小写,括号里写异常类型,例如 catch (Exception ex)。",
|
||||
("CS0246", var name) when name.StartsWith("bool") || name.StartsWith("private") || name.StartsWith("public") || name.StartsWith("string") || name.StartsWith("int") =>
|
||||
$"“{name}”像是类型和名字之间少了一个空格。",
|
||||
("CS0234", var s) when s.Contains("Properties") => "要用 Properties.Resources,先点代码上方的“项目资源”,添加需要的图片或声音。",
|
||||
("CS0117", var s) when s.Contains("Resources.") => $"项目资源里还没有“{s[(s.LastIndexOf('.') + 1)..]}”:点代码上方的“项目资源”添加(名字要和代码里一致)。",
|
||||
("CS0103", _) => $"找不到“{source}”。检查拼写和大小写(C# 区分大小写),以及控件的 Name 是否和代码里一致。",
|
||||
("CS1002", _) => "C# 每条语句末尾要有英文分号 ;",
|
||||
("CS1056", _) or ("CS1525", _) when source.Length == 1 && source[0] > 127 => "这里出现了中文标点,C# 代码里的符号要用英文半角。",
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.CSharp;
|
||||
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||
using Microsoft.CodeAnalysis.Operations;
|
||||
using Microsoft.CodeAnalysis.Text;
|
||||
|
||||
namespace Sim.Runner;
|
||||
|
||||
/// <summary>
|
||||
/// 编译前把传给 System.IO(File、Directory、Path、FileStream、StreamWriter 等)和 Image / Bitmap 读写文件方法的路径参数
|
||||
/// 包上 Sim.Host.WindowsPaths.Fix(...):教材按 Windows 写 AppDomain.CurrentDomain.BaseDirectory + "Camera\\",
|
||||
/// 在 Linux 上反斜杠要换成斜杠。只包语义上确实是这些方法里名字带 path / file / dir 的字符串参数;
|
||||
/// 改写不换行,报错和异常里的行号不变。学生在编辑器里看到的仍是原来的代码。
|
||||
/// </summary>
|
||||
internal static class PathRewriter
|
||||
{
|
||||
private const string Fix = "global::Sim.Host.WindowsPaths.Fix";
|
||||
|
||||
public static CSharpCompilation Rewrite(CSharpCompilation compilation)
|
||||
{
|
||||
var result = compilation;
|
||||
foreach (var tree in compilation.SyntaxTrees)
|
||||
{
|
||||
var model = compilation.GetSemanticModel(tree);
|
||||
var root = tree.GetRoot();
|
||||
var targets = root.DescendantNodes()
|
||||
.Where(n => n is InvocationExpressionSyntax or BaseObjectCreationExpressionSyntax)
|
||||
.SelectMany(n => PathArguments(model.GetOperation(n)))
|
||||
.ToHashSet();
|
||||
if (targets.Count == 0) continue;
|
||||
var rewritten = root.ReplaceNodes(targets, (_, node) =>
|
||||
SyntaxFactory.ParseExpression($"{Fix}({node.WithoutTrivia().ToFullString()})").WithTriviaFrom(node));
|
||||
result = result.ReplaceSyntaxTree(tree,
|
||||
CSharpSyntaxTree.ParseText(SourceText.From(rewritten.ToFullString(), Encoding.UTF8), (CSharpParseOptions)tree.Options, tree.FilePath));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>调用里要包起来的路径参数(只取学生直接写的表达式;不含反斜杠的字符串常量不用包)</summary>
|
||||
private static IEnumerable<ExpressionSyntax> PathArguments(IOperation? operation)
|
||||
{
|
||||
var (method, arguments) = operation switch
|
||||
{
|
||||
IInvocationOperation invocation => (invocation.TargetMethod, invocation.Arguments),
|
||||
IObjectCreationOperation creation when creation.Constructor != null => (creation.Constructor, creation.Arguments),
|
||||
_ => (null, default),
|
||||
};
|
||||
if (method == null || !HandlesFiles(method.ContainingType)) yield break;
|
||||
foreach (var argument in arguments)
|
||||
{
|
||||
if (argument.Parameter is not { } parameter || !IsPathName(parameter.Name)) continue;
|
||||
var values = argument.ArgumentKind == ArgumentKind.ParamArray && argument.Value is IArrayCreationOperation { Initializer: { } init }
|
||||
? init.ElementValues
|
||||
: [argument.Value];
|
||||
foreach (var value in values)
|
||||
{
|
||||
if (value.IsImplicit || value.Type?.SpecialType != SpecialType.System_String) continue;
|
||||
if (value.ConstantValue is { HasValue: true, Value: string constant } && !constant.Contains('\\')) continue;
|
||||
if (value.Syntax is ExpressionSyntax expression) yield return expression;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static bool HandlesFiles(INamedTypeSymbol type)
|
||||
{
|
||||
var ns = type.ContainingNamespace?.ToDisplayString();
|
||||
return ns == "System.IO" || (ns == "System.Drawing" && type.Name is "Image" or "Bitmap");
|
||||
}
|
||||
|
||||
private static bool IsPathName(string name) =>
|
||||
name.Contains("path", StringComparison.OrdinalIgnoreCase)
|
||||
|| name.Contains("file", StringComparison.OrdinalIgnoreCase)
|
||||
|| name.Contains("dir", StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
@@ -56,9 +56,10 @@ internal static class StudentCompiler
|
||||
|
||||
using var peStream = new MemoryStream();
|
||||
using var pdbStream = new MemoryStream();
|
||||
// 编译改写后的代码(旧版线程 API 换成平台实现),API 检查仍针对学生写的原始代码
|
||||
var emit = LegacyThreadRewriter.Rewrite(compilation).Emit(peStream, pdbStream,
|
||||
options: new EmitOptions(debugInformationFormat: DebugInformationFormat.PortablePdb));
|
||||
// 编译改写后的代码(旧版线程 API 换成平台实现,DateTime.Now 换成虚拟时钟),API 检查仍针对学生写的原始代码
|
||||
var emit = PathRewriter.Rewrite(ClockRewriter.Rewrite(LegacyThreadRewriter.Rewrite(compilation))).Emit(peStream, pdbStream,
|
||||
options: new EmitOptions(debugInformationFormat: DebugInformationFormat.PortablePdb),
|
||||
manifestResources: EmptyControlResources(compilation));
|
||||
var denied = ApiPolicy.Check(compilation).ToList();
|
||||
|
||||
var diagnostics = new JsonArray();
|
||||
@@ -71,6 +72,39 @@ internal static class StudentCompiler
|
||||
return new Result(emit.Success && denied.Count == 0, peStream.ToArray(), pdbStream.ToArray(), diagnostics);
|
||||
}
|
||||
|
||||
private static readonly Lazy<byte[]> EmptyResources = new(() =>
|
||||
{
|
||||
var stream = new MemoryStream();
|
||||
using (var writer = new System.Resources.ResourceWriter(stream)) writer.Generate();
|
||||
return stream.ToArray();
|
||||
});
|
||||
|
||||
/// <summary>
|
||||
/// 给学生工程里的每个窗体、控件类嵌一份空的 .resources:VS 生成的 Designer.cs 会用
|
||||
/// ComponentResourceManager(typeof(窗体)).GetObject("…OcxState") 读窗体的 .resx(如 Windows Media Player 控件),
|
||||
/// 平台没有 .resx,有了空资源时取到 null,而不是报"找不到资源"。
|
||||
/// </summary>
|
||||
private static List<ResourceDescription> EmptyControlResources(CSharpCompilation compilation)
|
||||
{
|
||||
var control = compilation.GetTypeByMetadataName("System.Windows.Forms.Control");
|
||||
var resources = new List<ResourceDescription>();
|
||||
if (control == null) return resources;
|
||||
var names = new HashSet<string>();
|
||||
foreach (var tree in compilation.SyntaxTrees)
|
||||
{
|
||||
var model = compilation.GetSemanticModel(tree);
|
||||
foreach (var declaration in tree.GetRoot().DescendantNodes().OfType<Microsoft.CodeAnalysis.CSharp.Syntax.ClassDeclarationSyntax>())
|
||||
{
|
||||
if (model.GetDeclaredSymbol(declaration) is not { } type) continue;
|
||||
var derives = false;
|
||||
for (var t = type.BaseType; t != null && !derives; t = t.BaseType) derives = SymbolEqualityComparer.Default.Equals(t, control);
|
||||
var name = type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat).Replace("global::", "") + ".resources";
|
||||
if (derives && names.Add(name)) resources.Add(new ResourceDescription(name, () => new MemoryStream(EmptyResources.Value), isPublic: true));
|
||||
}
|
||||
}
|
||||
return resources;
|
||||
}
|
||||
|
||||
internal static JsonObject ToJson(Diagnostic diagnostic)
|
||||
{
|
||||
var span = diagnostic.Location.GetMappedLineSpan();
|
||||
|
||||
@@ -104,6 +104,9 @@ internal static class StudentProgramHost
|
||||
baseDir = Directory.CreateDirectory(Path.Combine(Path.GetTempPath(), "StudentApp", "bin", "Debug")).FullName;
|
||||
}
|
||||
baseDir = Path.TrimEndingDirectorySeparator(baseDir) + Path.DirectorySeparatorChar;
|
||||
// 程序目录下的 Resources 文件夹指向素材库:教材用 BaseDirectory + "//Resources//ring.mp3" 播放铃声
|
||||
var resources = Path.Combine(baseDir, "Resources");
|
||||
if (!Path.Exists(resources)) Directory.CreateSymbolicLink(resources, ProjectResources.Directory);
|
||||
AppContext.SetData("APP_CONTEXT_BASE_DIRECTORY", baseDir);
|
||||
Directory.SetCurrentDirectory(baseDir);
|
||||
SimHost.AppBaseDirectory = baseDir;
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
namespace System.Drawing;
|
||||
|
||||
/// <summary>
|
||||
/// 绘图面。平台的控件由浏览器绘制,学生程序不能自己画(没有 DrawLine 等方法);
|
||||
/// 只为让 VS 生成的空 Paint 事件处理程序(panel1_Paint)能编译。
|
||||
/// </summary>
|
||||
public sealed class Graphics : IDisposable
|
||||
{
|
||||
private Graphics()
|
||||
{
|
||||
}
|
||||
|
||||
public void Dispose() => GC.SuppressFinalize(this);
|
||||
}
|
||||
@@ -0,0 +1,166 @@
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.Drawing;
|
||||
using System.IO;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text.Json.Nodes;
|
||||
using System.Windows.Forms;
|
||||
using Sim.Host;
|
||||
|
||||
namespace System.Windows.Forms
|
||||
{
|
||||
/// <summary>
|
||||
/// ActiveX 控件的基类。VS 把 COM 控件拖到窗体上时生成 OcxState = resources.GetObject("…OcxState"),
|
||||
/// 平台编译时给每个窗体嵌了空的资源,这里取到的是 null,不影响使用。
|
||||
/// </summary>
|
||||
public abstract class AxHost : Control, ISupportInitialize
|
||||
{
|
||||
protected AxHost(string clsid) { }
|
||||
|
||||
public State? OcxState { get; set; }
|
||||
|
||||
public void BeginInit() { }
|
||||
public void EndInit() { }
|
||||
|
||||
public class State { }
|
||||
}
|
||||
}
|
||||
|
||||
namespace WMPLib
|
||||
{
|
||||
public enum WMPPlayState
|
||||
{
|
||||
wmppsUndefined = 0, wmppsStopped = 1, wmppsPaused = 2, wmppsPlaying = 3, wmppsScanForward = 4, wmppsScanReverse = 5,
|
||||
wmppsBuffering = 6, wmppsWaiting = 7, wmppsMediaEnded = 8, wmppsTransitioning = 9, wmppsReady = 10, wmppsReconnecting = 11,
|
||||
}
|
||||
|
||||
/// <summary>播放控制:axWindowsMediaPlayer1.Ctlcontrols.play()</summary>
|
||||
public interface IWMPControls
|
||||
{
|
||||
void play();
|
||||
void pause();
|
||||
void stop();
|
||||
double currentPosition { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>播放设置:axWindowsMediaPlayer1.settings.volume</summary>
|
||||
public interface IWMPSettings
|
||||
{
|
||||
bool autoStart { get; set; }
|
||||
int volume { get; set; }
|
||||
bool mute { get; set; }
|
||||
void setMode(string bstrMode, bool varfMode);
|
||||
}
|
||||
}
|
||||
|
||||
namespace AxWMPLib
|
||||
{
|
||||
using WMPLib;
|
||||
|
||||
/// <summary>
|
||||
/// Windows Media Player 控件(教材项目 7 的"广播"播放 ring.mp3)。和真的一样:设置 URL 后自动开始播放(settings.autoStart),
|
||||
/// 也可以调用 Ctlcontrols.play()。声音在浏览器里播放;实训台上的广播喇叭随之亮起。只支持本机文件。
|
||||
/// </summary>
|
||||
[ClientType("AxWindowsMediaPlayer")]
|
||||
public class AxWindowsMediaPlayer : AxHost
|
||||
{
|
||||
private string _url = "";
|
||||
private WMPPlayState _playState = WMPPlayState.wmppsUndefined;
|
||||
|
||||
public AxWindowsMediaPlayer() : base("6bf52a52-394a-11d3-b153-00c04f79faa6")
|
||||
{
|
||||
Ctlcontrols = new PlayerControls(this);
|
||||
settings = new Settings();
|
||||
}
|
||||
|
||||
protected override Size DefaultSize => new(245, 240);
|
||||
|
||||
public string URL
|
||||
{
|
||||
get => _url;
|
||||
set
|
||||
{
|
||||
_url = value ?? "";
|
||||
if (settings.autoStart && _url.Length > 0) Play();
|
||||
}
|
||||
}
|
||||
|
||||
public IWMPControls Ctlcontrols { get; }
|
||||
|
||||
// 属性名和 COM 接口一样是小写
|
||||
public IWMPSettings settings { get; }
|
||||
|
||||
public WMPPlayState playState => _playState;
|
||||
|
||||
public string uiMode { get; set; } = "full";
|
||||
public bool stretchToFit { get; set; }
|
||||
public bool enableContextMenu { get; set; } = true;
|
||||
public string status => _playState == WMPPlayState.wmppsPlaying ? "正在播放" : "已停止";
|
||||
|
||||
public event EventHandler? PlayStateChange;
|
||||
|
||||
private void Play()
|
||||
{
|
||||
LegacyThreads.CheckPoint();
|
||||
// 教材写 BaseDirectory + "//Resources//ring.mp3":路径里多余的斜杠在 Linux 上也能用
|
||||
var path = _url.StartsWith("file:///", StringComparison.OrdinalIgnoreCase) ? new Uri(_url).LocalPath : _url;
|
||||
if (!File.Exists(path))
|
||||
{
|
||||
SimHost.SendConsole($"Windows Media Player:找不到要播放的文件 {_url}\n");
|
||||
SetState(WMPPlayState.wmppsStopped);
|
||||
return;
|
||||
}
|
||||
var data = File.ReadAllBytes(path);
|
||||
var mime = Path.GetExtension(path).ToLowerInvariant() switch
|
||||
{
|
||||
".wav" => "audio/wav",
|
||||
".ogg" => "audio/ogg",
|
||||
".m4a" or ".aac" => "audio/aac",
|
||||
_ => "audio/mpeg",
|
||||
};
|
||||
var key = SimHost.DataKey(Convert.ToHexString(SHA1.HashData(data)).ToLowerInvariant(), data, mime);
|
||||
SimHost.Send(new JsonObject { ["type"] = "media", ["id"] = SimId, ["action"] = "play", ["key"] = key, ["name"] = Path.GetFileName(path) });
|
||||
SetState(WMPPlayState.wmppsPlaying);
|
||||
}
|
||||
|
||||
private void Stop()
|
||||
{
|
||||
if (_playState != WMPPlayState.wmppsPlaying) return;
|
||||
SimHost.Send(new JsonObject { ["type"] = "media", ["id"] = SimId, ["action"] = "stop" });
|
||||
SetState(WMPPlayState.wmppsStopped);
|
||||
}
|
||||
|
||||
private void SetState(WMPPlayState state)
|
||||
{
|
||||
if (_playState == state) return;
|
||||
_playState = state;
|
||||
PlayStateChange?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
|
||||
protected override void WriteProps(JsonObject props)
|
||||
{
|
||||
base.WriteProps(props);
|
||||
props["URL"] = _url;
|
||||
}
|
||||
|
||||
private sealed class PlayerControls(AxWindowsMediaPlayer owner) : IWMPControls
|
||||
{
|
||||
public void play()
|
||||
{
|
||||
if (owner._playState != WMPPlayState.wmppsPlaying && owner._url.Length > 0) owner.Play();
|
||||
}
|
||||
|
||||
public void pause() => owner.Stop();
|
||||
public void stop() => owner.Stop();
|
||||
public double currentPosition { get; set; }
|
||||
}
|
||||
|
||||
private sealed class Settings : IWMPSettings
|
||||
{
|
||||
public bool autoStart { get; set; } = true;
|
||||
public int volume { get; set; } = 50;
|
||||
public bool mute { get; set; }
|
||||
public void setMode(string bstrMode, bool varfMode) { }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,481 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Data;
|
||||
using System.Drawing;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Text.Json.Nodes;
|
||||
using Sim.Host;
|
||||
|
||||
namespace System.Windows.Forms.DataVisualization.Charting;
|
||||
|
||||
public enum SeriesChartType
|
||||
{
|
||||
Point, FastPoint, Bubble, Line, Spline, StepLine, FastLine, Bar, StackedBar, StackedBar100, Column, StackedColumn, StackedColumn100,
|
||||
Area, SplineArea, StackedArea, StackedArea100, Pie, Doughnut, Stock, Candlestick, Range, SplineRange, RangeBar, RangeColumn,
|
||||
Radar, Polar, ErrorBar, BoxPlot, Renko, ThreeLineBreak, Kagi, PointAndFigure, Funnel, Pyramid,
|
||||
}
|
||||
|
||||
public enum ChartValueType { Auto, Double, Single, Int32, Int64, UInt32, UInt64, String, DateTime, Date, Time, DateTimeOffset }
|
||||
public enum Docking { Top, Right, Bottom, Left }
|
||||
public enum MarkerStyle { None, Square, Circle, Diamond, Triangle, Cross, Star4, Star5, Star6, Star10 }
|
||||
public enum ChartColorPalette { None, Bright, Grayscale, Excel, Light, Pastel, EarthTones, SemiTransparent, Berry, Chocolate, Fire, SeaGreen, BrightPastel }
|
||||
public enum StringAlignment { Near, Center, Far }
|
||||
|
||||
/// <summary>图表里的元素(ChartArea、Legend、Series、Title):有名字,改动时通知所在的图表重画</summary>
|
||||
public abstract class ChartNamedElement
|
||||
{
|
||||
private string _name = "";
|
||||
|
||||
internal Chart? Owner { get; set; }
|
||||
|
||||
public string Name
|
||||
{
|
||||
get => _name;
|
||||
set
|
||||
{
|
||||
_name = value ?? "";
|
||||
Changed();
|
||||
}
|
||||
}
|
||||
|
||||
public object? Tag { get; set; }
|
||||
|
||||
internal void Changed() => Owner?.Invalidated();
|
||||
}
|
||||
|
||||
/// <summary>ChartAreas、Legends、Series、Titles 共用:按序号或名字取,Add 一个对象或一个名字</summary>
|
||||
public class ChartNamedElementCollection<T> : IEnumerable<T> where T : ChartNamedElement, new()
|
||||
{
|
||||
private readonly List<T> _items = new();
|
||||
private readonly Chart _owner;
|
||||
private readonly string _prefix;
|
||||
|
||||
internal ChartNamedElementCollection(Chart owner, string prefix)
|
||||
{
|
||||
_owner = owner;
|
||||
_prefix = prefix;
|
||||
}
|
||||
|
||||
public int Count => _items.Count;
|
||||
public T this[int index] => _items[index];
|
||||
|
||||
public T this[string name] =>
|
||||
FindByName(name) ?? throw new ArgumentException($"在集合中找不到名为“{name}”的图表元素。");
|
||||
|
||||
public T? FindByName(string name) => _items.FirstOrDefault(i => i.Name == name);
|
||||
|
||||
public void Add(T item)
|
||||
{
|
||||
if (string.IsNullOrEmpty(item.Name)) item.Name = NextName();
|
||||
item.Owner = _owner;
|
||||
_items.Add(item);
|
||||
_owner.Invalidated();
|
||||
}
|
||||
|
||||
public T Add(string name)
|
||||
{
|
||||
var item = new T { Name = name };
|
||||
Add(item);
|
||||
return item;
|
||||
}
|
||||
|
||||
public int IndexOf(string name) => _items.FindIndex(i => i.Name == name);
|
||||
public bool Remove(T item)
|
||||
{
|
||||
var removed = _items.Remove(item);
|
||||
_owner.Invalidated();
|
||||
return removed;
|
||||
}
|
||||
|
||||
public void RemoveAt(int index)
|
||||
{
|
||||
_items.RemoveAt(index);
|
||||
_owner.Invalidated();
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
_items.Clear();
|
||||
_owner.Invalidated();
|
||||
}
|
||||
|
||||
private string NextName()
|
||||
{
|
||||
for (var i = 1; ; i++)
|
||||
if (FindByName($"{_prefix}{i}") == null) return $"{_prefix}{i}";
|
||||
}
|
||||
|
||||
public IEnumerator<T> GetEnumerator() => _items.GetEnumerator();
|
||||
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
|
||||
}
|
||||
|
||||
public class Axis
|
||||
{
|
||||
public string Title { get; set; } = "";
|
||||
public double Minimum { get; set; } = double.NaN;
|
||||
public double Maximum { get; set; } = double.NaN;
|
||||
public double Interval { get; set; }
|
||||
public Grid MajorGrid { get; } = new();
|
||||
public LabelStyle LabelStyle { get; } = new();
|
||||
public bool IsStartedFromZero { get; set; } = true;
|
||||
}
|
||||
|
||||
public class Grid
|
||||
{
|
||||
public bool Enabled { get; set; } = true;
|
||||
public Color LineColor { get; set; } = Color.Black;
|
||||
}
|
||||
|
||||
public class LabelStyle
|
||||
{
|
||||
public string Format { get; set; } = "";
|
||||
public int Angle { get; set; }
|
||||
}
|
||||
|
||||
public class ChartArea : ChartNamedElement
|
||||
{
|
||||
public ChartArea() { }
|
||||
public ChartArea(string name) => Name = name;
|
||||
|
||||
public Axis AxisX { get; } = new();
|
||||
public Axis AxisY { get; } = new();
|
||||
public Axis AxisX2 { get; } = new();
|
||||
public Axis AxisY2 { get; } = new();
|
||||
public Color BackColor { get; set; }
|
||||
}
|
||||
|
||||
public class Legend : ChartNamedElement
|
||||
{
|
||||
public Legend() { }
|
||||
public Legend(string name) => Name = name;
|
||||
|
||||
public bool Enabled { get; set; } = true;
|
||||
public Docking Docking { get; set; } = Docking.Right;
|
||||
public string Title { get; set; } = "";
|
||||
public Color TitleForeColor { get; set; } = Color.Black;
|
||||
public Color BackColor { get; set; }
|
||||
public Color ForeColor { get; set; } = Color.Black;
|
||||
public StringAlignment Alignment { get; set; }
|
||||
}
|
||||
|
||||
public class Title : ChartNamedElement
|
||||
{
|
||||
private string _text = "";
|
||||
|
||||
public Title() { }
|
||||
public Title(string text) => _text = text;
|
||||
|
||||
public string Text
|
||||
{
|
||||
get => _text;
|
||||
set
|
||||
{
|
||||
_text = value ?? "";
|
||||
Changed();
|
||||
}
|
||||
}
|
||||
|
||||
public Font? Font { get; set; }
|
||||
public Color ForeColor { get; set; } = Color.Black;
|
||||
public Docking Docking { get; set; } = Docking.Top;
|
||||
}
|
||||
|
||||
public class DataPoint
|
||||
{
|
||||
public DataPoint() { }
|
||||
|
||||
public DataPoint(double xValue, double yValue)
|
||||
{
|
||||
XValue = xValue;
|
||||
YValues = new[] { yValue };
|
||||
}
|
||||
|
||||
public double XValue { get; set; }
|
||||
public double[] YValues { get; set; } = { 0 };
|
||||
/// <summary>X 值是文字(如日期字符串)时显示的标签</summary>
|
||||
public string AxisLabel { get; set; } = "";
|
||||
public string Label { get; set; } = "";
|
||||
public Color Color { get; set; }
|
||||
public bool IsEmpty { get; set; }
|
||||
public string ToolTip { get; set; } = "";
|
||||
}
|
||||
|
||||
public class DataPointCollection : IEnumerable<DataPoint>
|
||||
{
|
||||
private readonly List<DataPoint> _items = new();
|
||||
private readonly Series _series;
|
||||
|
||||
internal DataPointCollection(Series series) => _series = series;
|
||||
|
||||
public int Count => _items.Count;
|
||||
public DataPoint this[int index] => _items[index];
|
||||
|
||||
/// <summary>加一个点:X 是数字或日期时按数值,否则(如字符串)当作分类标签,和 WinForms 一样</summary>
|
||||
public int AddXY(object? xValue, params object?[] yValue)
|
||||
{
|
||||
var point = new DataPoint { YValues = yValue.Select(ToDouble).ToArray() };
|
||||
switch (xValue)
|
||||
{
|
||||
case null or DBNull:
|
||||
break;
|
||||
case DateTime d:
|
||||
point.XValue = d.ToOADate();
|
||||
point.AxisLabel = d.ToString(CultureInfo.CurrentCulture);
|
||||
break;
|
||||
case IConvertible c when xValue is not string:
|
||||
point.XValue = c.ToDouble(CultureInfo.InvariantCulture);
|
||||
break;
|
||||
default:
|
||||
point.AxisLabel = Convert.ToString(xValue, CultureInfo.CurrentCulture) ?? "";
|
||||
point.XValue = _items.Count + 1;
|
||||
break;
|
||||
}
|
||||
_items.Add(point);
|
||||
_series.Changed();
|
||||
return _items.Count - 1;
|
||||
}
|
||||
|
||||
public int AddY(params object?[] yValue) => AddXY(_items.Count + 1, yValue);
|
||||
|
||||
public void Add(DataPoint point)
|
||||
{
|
||||
_items.Add(point);
|
||||
_series.Changed();
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
_items.Clear();
|
||||
_series.Changed();
|
||||
}
|
||||
|
||||
public void RemoveAt(int index)
|
||||
{
|
||||
_items.RemoveAt(index);
|
||||
_series.Changed();
|
||||
}
|
||||
|
||||
/// <summary>按两组值绑定:xValues 和 yValues 一一对应</summary>
|
||||
public void DataBindXY(IEnumerable xValues, params IEnumerable[] yValues)
|
||||
{
|
||||
_items.Clear();
|
||||
var xs = xValues.Cast<object?>().ToList();
|
||||
var ys = yValues.Select(y => y.Cast<object?>().ToList()).ToList();
|
||||
for (var i = 0; i < xs.Count; i++) AddXY(xs[i], ys.Select(y => i < y.Count ? y[i] : null).ToArray());
|
||||
_series.Changed();
|
||||
}
|
||||
|
||||
public void DataBindY(params IEnumerable[] yValues) => DataBindXY(Enumerable.Range(1, yValues[0].Cast<object>().Count()).ToList(), yValues);
|
||||
|
||||
internal static double ToDouble(object? value) => value switch
|
||||
{
|
||||
null or DBNull => double.NaN,
|
||||
DateTime d => d.ToOADate(),
|
||||
IConvertible c => double.TryParse(Convert.ToString(c, CultureInfo.InvariantCulture), NumberStyles.Float, CultureInfo.InvariantCulture, out var v) ? v : double.NaN,
|
||||
_ => double.NaN,
|
||||
};
|
||||
|
||||
public IEnumerator<DataPoint> GetEnumerator() => _items.GetEnumerator();
|
||||
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
|
||||
}
|
||||
|
||||
public class Series : ChartNamedElement
|
||||
{
|
||||
private SeriesChartType _chartType = SeriesChartType.Column;
|
||||
private string _legendText = "";
|
||||
private Color _color;
|
||||
|
||||
public Series()
|
||||
{
|
||||
Points = new DataPointCollection(this);
|
||||
}
|
||||
|
||||
public Series(string name) : this() => Name = name;
|
||||
|
||||
public DataPointCollection Points { get; }
|
||||
|
||||
public SeriesChartType ChartType
|
||||
{
|
||||
get => _chartType;
|
||||
set
|
||||
{
|
||||
_chartType = value;
|
||||
Changed();
|
||||
}
|
||||
}
|
||||
|
||||
public string ChartArea { get; set; } = "";
|
||||
public string Legend { get; set; } = "";
|
||||
|
||||
public string LegendText
|
||||
{
|
||||
get => _legendText;
|
||||
set
|
||||
{
|
||||
_legendText = value ?? "";
|
||||
Changed();
|
||||
}
|
||||
}
|
||||
|
||||
public string XValueMember { get; set; } = "";
|
||||
public string YValueMembers { get; set; } = "";
|
||||
public ChartValueType XValueType { get; set; }
|
||||
public ChartValueType YValueType { get; set; }
|
||||
public int YValuesPerPoint { get; set; } = 1;
|
||||
public bool IsVisibleInLegend { get; set; } = true;
|
||||
public bool IsValueShownAsLabel { get; set; }
|
||||
public bool Enabled { get; set; } = true;
|
||||
public int BorderWidth { get; set; } = 1;
|
||||
public MarkerStyle MarkerStyle { get; set; }
|
||||
public int MarkerSize { get; set; } = 5;
|
||||
public Font? Font { get; set; }
|
||||
public string Label { get; set; } = "";
|
||||
public string ToolTip { get; set; } = "";
|
||||
|
||||
public Color Color
|
||||
{
|
||||
get => _color;
|
||||
set
|
||||
{
|
||||
_color = value;
|
||||
Changed();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 图表(教材项目 8 显示最近 7 天的最高温和平均温)。和 WinForms 的 Chart 一样,设置 DataSource 后调用 DataBind()
|
||||
/// 按各 Series 的 XValueMember / YValueMembers 取数据,也可以用 Points.AddXY 自己加点。浏览器用 Chart.js 画。
|
||||
/// </summary>
|
||||
public class Chart : Control, ISupportInitialize
|
||||
{
|
||||
private bool _initializing;
|
||||
private bool _dirty;
|
||||
|
||||
public Chart()
|
||||
{
|
||||
ChartAreas = new ChartNamedElementCollection<ChartArea>(this, "ChartArea");
|
||||
Legends = new ChartNamedElementCollection<Legend>(this, "Legend");
|
||||
Series = new ChartNamedElementCollection<Series>(this, "Series");
|
||||
Titles = new ChartNamedElementCollection<Title>(this, "Title");
|
||||
BackColor = Color.White;
|
||||
}
|
||||
|
||||
protected override Size DefaultSize => new(300, 300);
|
||||
|
||||
public ChartNamedElementCollection<ChartArea> ChartAreas { get; }
|
||||
public ChartNamedElementCollection<Legend> Legends { get; }
|
||||
public ChartNamedElementCollection<Series> Series { get; }
|
||||
public ChartNamedElementCollection<Title> Titles { get; }
|
||||
public object? DataSource { get; set; }
|
||||
public ChartColorPalette Palette { get; set; } = ChartColorPalette.BrightPastel;
|
||||
public Color BorderlineColor { get; set; }
|
||||
public int BorderlineWidth { get; set; } = 1;
|
||||
|
||||
public void BeginInit() => _initializing = true;
|
||||
|
||||
public void EndInit()
|
||||
{
|
||||
_initializing = false;
|
||||
Invalidated();
|
||||
}
|
||||
|
||||
/// <summary>按 DataSource 重新取数据:每个设置了 YValueMembers 的 Series 清空后,按行加点</summary>
|
||||
public void DataBind()
|
||||
{
|
||||
var rows = Rows(DataSource);
|
||||
foreach (var series in Series)
|
||||
{
|
||||
if (string.IsNullOrEmpty(series.YValueMembers)) continue;
|
||||
series.Points.Clear();
|
||||
var yMembers = series.YValueMembers.Split(',').Select(m => m.Trim()).ToArray();
|
||||
foreach (var row in rows)
|
||||
{
|
||||
var x = string.IsNullOrEmpty(series.XValueMember) ? null : Field(row, series.XValueMember);
|
||||
series.Points.AddXY(x ?? series.Points.Count + 1, yMembers.Select(m => Field(row, m)).ToArray());
|
||||
}
|
||||
}
|
||||
Invalidated();
|
||||
}
|
||||
|
||||
private static List<object> Rows(object? source) => source switch
|
||||
{
|
||||
null => new List<object>(),
|
||||
DataSet set => set.Tables.Count > 0 ? set.Tables[0].Rows.Cast<object>().ToList() : new List<object>(),
|
||||
DataTable table => table.Rows.Cast<object>().ToList(),
|
||||
DataView view => view.Cast<object>().ToList(),
|
||||
IEnumerable list => list.Cast<object>().ToList(),
|
||||
_ => throw new ArgumentException("图表的 DataSource 要是 DataTable、DataView、DataSet 或列表。"),
|
||||
};
|
||||
|
||||
private static object? Field(object row, string member)
|
||||
{
|
||||
try
|
||||
{
|
||||
return row switch
|
||||
{
|
||||
DataRow r => r[member],
|
||||
DataRowView v => v[member],
|
||||
_ => row.GetType().GetProperty(member)?.GetValue(row),
|
||||
};
|
||||
}
|
||||
catch (ArgumentException)
|
||||
{
|
||||
throw new ArgumentException($"数据源里没有名为“{member}”的列(检查 XValueMember / YValueMembers)。");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>图表内容变了:攒到这一轮消息处理结束时再发给浏览器</summary>
|
||||
internal void Invalidated()
|
||||
{
|
||||
if (_initializing || !IsAttached || _dirty) return;
|
||||
_dirty = true;
|
||||
SimHost.Ui.Post(() =>
|
||||
{
|
||||
_dirty = false;
|
||||
NotifyChanged("Chart", ChartJson());
|
||||
});
|
||||
}
|
||||
|
||||
private JsonObject ChartJson()
|
||||
{
|
||||
var series = new JsonArray();
|
||||
foreach (var s in Series)
|
||||
{
|
||||
if (!s.Enabled) continue;
|
||||
var points = new JsonArray();
|
||||
foreach (var p in s.Points)
|
||||
points.Add(new JsonObject { ["x"] = p.AxisLabel.Length > 0 ? p.AxisLabel : p.XValue, ["y"] = p.YValues.Length > 0 && !double.IsNaN(p.YValues[0]) ? p.YValues[0] : null });
|
||||
series.Add(new JsonObject
|
||||
{
|
||||
["name"] = s.Name,
|
||||
["type"] = s.ChartType.ToString(),
|
||||
["legend"] = s.LegendText.Length > 0 ? s.LegendText : s.Name,
|
||||
["inLegend"] = s.IsVisibleInLegend && Legends.Count > 0 && Legends.Any(l => l.Enabled),
|
||||
["color"] = PropValue.ColorCss(s.Color),
|
||||
["width"] = s.BorderWidth,
|
||||
["labels"] = s.IsValueShownAsLabel,
|
||||
["points"] = points,
|
||||
});
|
||||
}
|
||||
var titles = new JsonArray();
|
||||
foreach (var t in Titles) titles.Add(t.Text);
|
||||
var area = ChartAreas.Count > 0 ? ChartAreas[0] : null;
|
||||
return new JsonObject
|
||||
{
|
||||
["series"] = series,
|
||||
["titles"] = titles,
|
||||
["legendDocking"] = Legends.Count > 0 ? Legends[0].Docking.ToString() : "Right",
|
||||
["yMin"] = area != null && !double.IsNaN(area.AxisY.Minimum) ? area.AxisY.Minimum : null,
|
||||
["yMax"] = area != null && !double.IsNaN(area.AxisY.Maximum) ? area.AxisY.Maximum : null,
|
||||
};
|
||||
}
|
||||
|
||||
protected override void WriteProps(JsonObject props)
|
||||
{
|
||||
base.WriteProps(props);
|
||||
props["Chart"] = ChartJson();
|
||||
}
|
||||
}
|
||||
@@ -43,7 +43,21 @@ public class Panel : ScrollableControl
|
||||
public BorderStyle BorderStyle
|
||||
{
|
||||
get => _borderStyle;
|
||||
set => SetProp(ref _borderStyle, value, nameof(BorderStyle));
|
||||
set
|
||||
{
|
||||
if (SetProp(ref _borderStyle, value, nameof(BorderStyle))) PerformLayout();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>边框占掉的宽度:单线 1 像素,立体 2 像素</summary>
|
||||
public override Rectangle DisplayRectangle
|
||||
{
|
||||
get
|
||||
{
|
||||
var inset = _borderStyle switch { BorderStyle.FixedSingle => 1, BorderStyle.Fixed3D => 2, _ => 0 };
|
||||
var rect = base.DisplayRectangle;
|
||||
return new Rectangle(rect.X, rect.Y, Math.Max(0, rect.Width - 2 * inset), Math.Max(0, rect.Height - 2 * inset));
|
||||
}
|
||||
}
|
||||
|
||||
protected override void WriteProps(JsonObject props)
|
||||
@@ -58,4 +72,14 @@ public class GroupBox : Control
|
||||
protected override Size DefaultSize => new(200, 100);
|
||||
|
||||
public FlatStyle FlatStyle { get; set; } = FlatStyle.Standard;
|
||||
|
||||
/// <summary>分组框的边框和标题占掉的地方(宋体 9pt 时和 WinForms 一致)</summary>
|
||||
public override Rectangle DisplayRectangle
|
||||
{
|
||||
get
|
||||
{
|
||||
var rect = base.DisplayRectangle;
|
||||
return new Rectangle(rect.X + 3, rect.Y + 17, Math.Max(0, rect.Width - 6), Math.Max(0, rect.Height - 20));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -28,6 +28,10 @@ public class Control : Component, IWin32Window
|
||||
private Font? _font;
|
||||
private bool _autoSize;
|
||||
private DockStyle _dock;
|
||||
private Padding _padding;
|
||||
private int _layoutSuspendCount;
|
||||
private Image? _backgroundImage;
|
||||
private ImageLayout _backgroundImageLayout = ImageLayout.Tile;
|
||||
private AnchorStyles _anchor = AnchorStyles.Top | AnchorStyles.Left;
|
||||
private Control? _parent;
|
||||
|
||||
@@ -131,7 +135,13 @@ public class Control : Component, IWin32Window
|
||||
public Size Size
|
||||
{
|
||||
get => _size;
|
||||
set => SetProp(ref _size, value, nameof(Size));
|
||||
set
|
||||
{
|
||||
if (!SetProp(ref _size, value, nameof(Size))) return;
|
||||
// 大小变了:自己的停靠子控件重新排;自己是停靠的,父容器也要重新排
|
||||
PerformLayout();
|
||||
if (_dock != DockStyle.None) _parent?.PerformLayout();
|
||||
}
|
||||
}
|
||||
|
||||
public int Left { get => _location.X; set => Location = new Point(value, _location.Y); }
|
||||
@@ -162,7 +172,9 @@ public class Control : Component, IWin32Window
|
||||
get => _visible;
|
||||
set
|
||||
{
|
||||
if (SetProp(ref _visible, value, nameof(Visible))) OnVisibleChanged(EventArgs.Empty);
|
||||
if (!SetProp(ref _visible, value, nameof(Visible))) return;
|
||||
if (_dock != DockStyle.None) _parent?.PerformLayout();
|
||||
OnVisibleChanged(EventArgs.Empty);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -202,7 +214,40 @@ public class Control : Component, IWin32Window
|
||||
public DockStyle Dock
|
||||
{
|
||||
get => _dock;
|
||||
set => SetProp(ref _dock, value, nameof(Dock));
|
||||
set
|
||||
{
|
||||
if (SetProp(ref _dock, value, nameof(Dock))) _parent?.PerformLayout();
|
||||
}
|
||||
}
|
||||
|
||||
public Padding Padding
|
||||
{
|
||||
get => _padding;
|
||||
set
|
||||
{
|
||||
if (_padding.Equals(value)) return;
|
||||
_padding = value;
|
||||
PerformLayout();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>背景图片;BackgroundImageLayout 决定平铺(默认)、居中、拉伸或按比例缩放</summary>
|
||||
public virtual Image? BackgroundImage
|
||||
{
|
||||
get => _backgroundImage;
|
||||
set
|
||||
{
|
||||
LegacyThreads.CheckPoint();
|
||||
if (ReferenceEquals(_backgroundImage, value)) return;
|
||||
_backgroundImage = value;
|
||||
NotifyChanged(nameof(BackgroundImage), value);
|
||||
}
|
||||
}
|
||||
|
||||
public virtual ImageLayout BackgroundImageLayout
|
||||
{
|
||||
get => _backgroundImageLayout;
|
||||
set => SetProp(ref _backgroundImageLayout, value, nameof(BackgroundImageLayout));
|
||||
}
|
||||
|
||||
public AnchorStyles Anchor
|
||||
@@ -216,7 +261,6 @@ public class Control : Component, IWin32Window
|
||||
public bool TabStop { get; set; } = true;
|
||||
public object? Tag { get; set; }
|
||||
public Padding Margin { get; set; } = new(3);
|
||||
public Padding Padding { get; set; }
|
||||
public ImeMode ImeMode { get; set; } = ImeMode.NoControl;
|
||||
public virtual RightToLeft RightToLeft { get; set; } = RightToLeft.No;
|
||||
public virtual bool AllowDrop { get; set; }
|
||||
@@ -246,6 +290,8 @@ public class Control : Component, IWin32Window
|
||||
public event EventHandler? LostFocus;
|
||||
public event EventHandler? Enter;
|
||||
public event EventHandler? Leave;
|
||||
/// <summary>重绘。平台由浏览器绘制控件,不会触发这个事件(VS 双击 Panel 生成的空处理程序照常能编译)</summary>
|
||||
public event PaintEventHandler? Paint;
|
||||
|
||||
protected virtual void OnClick(EventArgs e) => Click?.Invoke(this, e);
|
||||
protected virtual void OnDoubleClick(EventArgs e) => DoubleClick?.Invoke(this, e);
|
||||
@@ -259,6 +305,7 @@ public class Control : Component, IWin32Window
|
||||
protected virtual void OnEnabledChanged(EventArgs e) => EnabledChanged?.Invoke(this, e);
|
||||
protected virtual void OnGotFocus(EventArgs e) => GotFocus?.Invoke(this, e);
|
||||
protected virtual void OnLostFocus(EventArgs e) => LostFocus?.Invoke(this, e);
|
||||
protected virtual void OnPaint(PaintEventArgs e) => Paint?.Invoke(this, e);
|
||||
protected virtual void OnEnter(EventArgs e) => Enter?.Invoke(this, e);
|
||||
protected virtual void OnLeave(EventArgs e) => Leave?.Invoke(this, e);
|
||||
|
||||
@@ -305,10 +352,65 @@ public class Control : Component, IWin32Window
|
||||
public void Refresh() { }
|
||||
public void Invalidate() { }
|
||||
public void Update() { }
|
||||
public void SuspendLayout() { }
|
||||
public void ResumeLayout() { }
|
||||
public void ResumeLayout(bool performLayout) { }
|
||||
public void PerformLayout() { }
|
||||
public void SuspendLayout() => _layoutSuspendCount++;
|
||||
public void ResumeLayout() => ResumeLayout(true);
|
||||
|
||||
public void ResumeLayout(bool performLayout)
|
||||
{
|
||||
if (_layoutSuspendCount > 0) _layoutSuspendCount--;
|
||||
if (performLayout) PerformLayout();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 停靠布局(和 WinForms 一样):按 Controls 集合从后往前,Top / Bottom / Left / Right 依次贴边并占掉那一条,
|
||||
/// Fill 占剩下的全部。VS 在 Designer.cs 里写的位置大小就是这样排出来的;代码里改停靠、大小、可见时重新排。
|
||||
/// </summary>
|
||||
public void PerformLayout()
|
||||
{
|
||||
if (_layoutSuspendCount > 0) return;
|
||||
var rect = DisplayRectangle;
|
||||
for (var i = Controls.Count - 1; i >= 0; i--)
|
||||
{
|
||||
var child = Controls[i];
|
||||
if (!child._visible || child._dock == DockStyle.None) continue;
|
||||
var size = child._size;
|
||||
Rectangle bounds;
|
||||
switch (child._dock)
|
||||
{
|
||||
case DockStyle.Top:
|
||||
bounds = new Rectangle(rect.X, rect.Y, rect.Width, size.Height);
|
||||
rect = new Rectangle(rect.X, rect.Y + size.Height, rect.Width, Math.Max(0, rect.Height - size.Height));
|
||||
break;
|
||||
case DockStyle.Bottom:
|
||||
bounds = new Rectangle(rect.X, rect.Bottom - size.Height, rect.Width, size.Height);
|
||||
rect = new Rectangle(rect.X, rect.Y, rect.Width, Math.Max(0, rect.Height - size.Height));
|
||||
break;
|
||||
case DockStyle.Left:
|
||||
bounds = new Rectangle(rect.X, rect.Y, size.Width, rect.Height);
|
||||
rect = new Rectangle(rect.X + size.Width, rect.Y, Math.Max(0, rect.Width - size.Width), rect.Height);
|
||||
break;
|
||||
case DockStyle.Right:
|
||||
bounds = new Rectangle(rect.Right - size.Width, rect.Y, size.Width, rect.Height);
|
||||
rect = new Rectangle(rect.X, rect.Y, Math.Max(0, rect.Width - size.Width), rect.Height);
|
||||
break;
|
||||
default:
|
||||
bounds = rect;
|
||||
break;
|
||||
}
|
||||
child.SetBoundsFromLayout(bounds);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>停靠布局给出的位置大小:不再反过来触发父容器的布局</summary>
|
||||
private void SetBoundsFromLayout(Rectangle bounds)
|
||||
{
|
||||
SetProp(ref _location, bounds.Location, nameof(Location));
|
||||
if (SetProp(ref _size, bounds.Size, nameof(Size))) PerformLayout();
|
||||
}
|
||||
|
||||
/// <summary>容器里用来停靠子控件的区域:客户区去掉内边距(分组框、带边框的面板再去掉边框)</summary>
|
||||
public virtual Rectangle DisplayRectangle =>
|
||||
new(_padding.Left, _padding.Top, Math.Max(0, ClientSize.Width - _padding.Horizontal), Math.Max(0, ClientSize.Height - _padding.Vertical));
|
||||
|
||||
public bool InvokeRequired => !SimHost.Ui.IsUiThread;
|
||||
|
||||
@@ -409,6 +511,8 @@ public class Control : Component, IWin32Window
|
||||
props["Font"] = PropValue.From(_font);
|
||||
props["AutoSize"] = _autoSize;
|
||||
props["Dock"] = _dock.ToString();
|
||||
props["BackgroundImage"] = PropValue.From(_backgroundImage);
|
||||
props["BackgroundImageLayout"] = _backgroundImageLayout.ToString();
|
||||
}
|
||||
|
||||
/// <summary>浏览器按这个类型名选择渲染方式。学生自己继承的控件按最近的仿真库基类渲染。</summary>
|
||||
@@ -459,6 +563,7 @@ public class Control : Component, IWin32Window
|
||||
value.Parent?.Controls.Remove(value);
|
||||
_items.Add(value);
|
||||
value.SetParentInternal(Owner);
|
||||
if (value.Dock != DockStyle.None) Owner.PerformLayout();
|
||||
if (Owner.IsAttached)
|
||||
{
|
||||
SimHost.Send(new JsonObject
|
||||
@@ -481,6 +586,7 @@ public class Control : Component, IWin32Window
|
||||
if (value == null || !_items.Remove(value)) return;
|
||||
var wasAttached = Owner.IsAttached;
|
||||
value.SetParentInternal(null);
|
||||
if (value.Dock != DockStyle.None) Owner.PerformLayout();
|
||||
if (wasAttached) SimHost.Send(new JsonObject { ["type"] = "controlRemoved", ["id"] = value.SimId });
|
||||
}
|
||||
|
||||
|
||||
@@ -626,8 +626,10 @@ public class DataGridView : Control, ISupportInitialize
|
||||
public event DataGridViewCellEventHandler? CellContentDoubleClick;
|
||||
public event DataGridViewCellEventHandler? CellValueChanged;
|
||||
public event DataGridViewCellEventHandler? CellEndEdit;
|
||||
#pragma warning disable CS0067 // 只读表格没有当前单元格的概念,这两个事件不触发
|
||||
public event DataGridViewCellEventHandler? CellEnter;
|
||||
public event DataGridViewCellEventHandler? RowEnter;
|
||||
#pragma warning restore CS0067
|
||||
public event DataGridViewDataErrorEventHandler? DataError;
|
||||
public event EventHandler? SelectionChanged;
|
||||
public event EventHandler? CurrentCellChanged;
|
||||
|
||||
@@ -13,7 +13,7 @@ public class DateTimePicker : Control
|
||||
public static readonly DateTime MinimumDateTime = new(1753, 1, 1);
|
||||
public static readonly DateTime MaximumDateTime = new(9998, 12, 31);
|
||||
|
||||
private DateTime _value = DateTime.Now;
|
||||
private DateTime _value = SimClock.Now;
|
||||
private DateTimePickerFormat _format = DateTimePickerFormat.Long;
|
||||
private string? _customFormat;
|
||||
private bool _showUpDown;
|
||||
|
||||
@@ -40,6 +40,8 @@ public enum ComboBoxStyle { Simple, DropDown, DropDownList }
|
||||
|
||||
public enum DockStyle { None, Top, Bottom, Left, Right, Fill }
|
||||
|
||||
public enum ImageLayout { None, Tile, Center, Stretch, Zoom }
|
||||
|
||||
[Flags]
|
||||
public enum AnchorStyles { None = 0, Top = 1, Bottom = 2, Left = 4, Right = 8 }
|
||||
|
||||
|
||||
@@ -41,3 +41,20 @@ public class FormClosedEventArgs : EventArgs
|
||||
}
|
||||
|
||||
public delegate void FormClosedEventHandler(object? sender, FormClosedEventArgs e);
|
||||
|
||||
/// <summary>重绘事件的参数。平台由浏览器绘制控件,不支持在 Graphics 上自己画</summary>
|
||||
public class PaintEventArgs : EventArgs, IDisposable
|
||||
{
|
||||
public PaintEventArgs(System.Drawing.Graphics graphics, System.Drawing.Rectangle clipRect)
|
||||
{
|
||||
Graphics = graphics;
|
||||
ClipRectangle = clipRect;
|
||||
}
|
||||
|
||||
public System.Drawing.Graphics Graphics { get; }
|
||||
public System.Drawing.Rectangle ClipRectangle { get; }
|
||||
|
||||
public void Dispose() => GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
public delegate void PaintEventHandler(object? sender, PaintEventArgs e);
|
||||
@@ -33,13 +33,16 @@ public class Form : ContainerControl
|
||||
public override Size ClientSize
|
||||
{
|
||||
get => _clientSize;
|
||||
set => SetProp(ref _clientSize, value, nameof(ClientSize));
|
||||
set
|
||||
{
|
||||
if (SetProp(ref _clientSize, value, nameof(ClientSize))) PerformLayout();
|
||||
}
|
||||
}
|
||||
|
||||
public new Size Size
|
||||
{
|
||||
get => _clientSize + NonClientSize;
|
||||
set => ClientSize = value - NonClientSize;
|
||||
get => _formBorderStyle == FormBorderStyle.None ? _clientSize : _clientSize + NonClientSize;
|
||||
set => ClientSize = _formBorderStyle == FormBorderStyle.None ? value : value - NonClientSize;
|
||||
}
|
||||
|
||||
public FormBorderStyle FormBorderStyle
|
||||
|
||||
@@ -0,0 +1,123 @@
|
||||
using System.ComponentModel;
|
||||
using System.Drawing;
|
||||
using System.Text.Json.Nodes;
|
||||
using Sim.Host;
|
||||
|
||||
namespace System.Windows.Forms;
|
||||
|
||||
public enum TickStyle { None, TopLeft, BottomRight, Both }
|
||||
|
||||
public enum Orientation { Horizontal, Vertical }
|
||||
|
||||
/// <summary>滑动条(教材项目 7 模拟空调温度、窗帘开合)</summary>
|
||||
public class TrackBar : Control, ISupportInitialize
|
||||
{
|
||||
private int _minimum;
|
||||
private int _maximum = 10;
|
||||
private int _value;
|
||||
private int _tickFrequency = 1;
|
||||
private TickStyle _tickStyle = TickStyle.BottomRight;
|
||||
private Orientation _orientation = Orientation.Horizontal;
|
||||
private bool _initializing;
|
||||
|
||||
public TrackBar() => AutoSize = true;
|
||||
|
||||
protected override Size DefaultSize => new(104, 45);
|
||||
|
||||
public int Minimum
|
||||
{
|
||||
get => _minimum;
|
||||
set => SetRange(value, Math.Max(value, _maximum));
|
||||
}
|
||||
|
||||
public int Maximum
|
||||
{
|
||||
get => _maximum;
|
||||
set => SetRange(Math.Min(value, _minimum), value);
|
||||
}
|
||||
|
||||
public int Value
|
||||
{
|
||||
get => _value;
|
||||
set
|
||||
{
|
||||
if (!_initializing && (value < _minimum || value > _maximum))
|
||||
throw new ArgumentOutOfRangeException(nameof(value), $"“{value}”的值对于“Value”无效。“Value”应介于 'Minimum' 和 'Maximum' 之间。");
|
||||
if (SetProp(ref _value, value, nameof(Value))) OnValueChanged(EventArgs.Empty);
|
||||
}
|
||||
}
|
||||
|
||||
public int TickFrequency
|
||||
{
|
||||
get => _tickFrequency;
|
||||
set => SetProp(ref _tickFrequency, value, nameof(TickFrequency));
|
||||
}
|
||||
|
||||
public TickStyle TickStyle
|
||||
{
|
||||
get => _tickStyle;
|
||||
set => SetProp(ref _tickStyle, value, nameof(TickStyle));
|
||||
}
|
||||
|
||||
public Orientation Orientation
|
||||
{
|
||||
get => _orientation;
|
||||
set
|
||||
{
|
||||
if (value == _orientation) return;
|
||||
SetProp(ref _orientation, value, nameof(Orientation));
|
||||
Size = new Size(Height, Width);
|
||||
}
|
||||
}
|
||||
|
||||
public int LargeChange { get; set; } = 5;
|
||||
public int SmallChange { get; set; } = 1;
|
||||
|
||||
public event EventHandler? Scroll;
|
||||
public event EventHandler? ValueChanged;
|
||||
|
||||
protected virtual void OnScroll(EventArgs e) => Scroll?.Invoke(this, e);
|
||||
protected virtual void OnValueChanged(EventArgs e) => ValueChanged?.Invoke(this, e);
|
||||
|
||||
public void SetRange(int minValue, int maxValue)
|
||||
{
|
||||
SetProp(ref _minimum, minValue, nameof(Minimum));
|
||||
SetProp(ref _maximum, Math.Max(minValue, maxValue), nameof(Maximum));
|
||||
if (_value < _minimum) Value = _minimum;
|
||||
else if (_value > _maximum) Value = _maximum;
|
||||
}
|
||||
|
||||
// Designer.cs 里 VS 生成 ((ISupportInitialize)trackBar1).BeginInit() / EndInit():期间先设 Value 再设范围也不报错
|
||||
public void BeginInit() => _initializing = true;
|
||||
|
||||
public void EndInit()
|
||||
{
|
||||
_initializing = false;
|
||||
if (_value < _minimum || _value > _maximum) Value = Math.Clamp(_value, _minimum, _maximum);
|
||||
}
|
||||
|
||||
/// <summary>用户拖动滑块、点轨道或按方向键:和 WinForms 一样先改 Value(ValueChanged),再触发 Scroll</summary>
|
||||
protected override void OnUserAction(UserAction action)
|
||||
{
|
||||
if (action.Action != "scroll" || !int.TryParse(action.Value, out var value))
|
||||
{
|
||||
base.OnUserAction(action);
|
||||
return;
|
||||
}
|
||||
Value = Math.Clamp(value, _minimum, _maximum);
|
||||
OnScroll(EventArgs.Empty);
|
||||
}
|
||||
|
||||
protected override void WriteProps(JsonObject props)
|
||||
{
|
||||
base.WriteProps(props);
|
||||
props["Minimum"] = _minimum;
|
||||
props["Maximum"] = _maximum;
|
||||
props["Value"] = _value;
|
||||
props["TickFrequency"] = _tickFrequency;
|
||||
props["TickStyle"] = _tickStyle.ToString();
|
||||
props["Orientation"] = _orientation.ToString();
|
||||
props["LargeChange"] = LargeChange;
|
||||
props["SmallChange"] = SmallChange;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
using System.ComponentModel;
|
||||
using System.Drawing;
|
||||
using System.IO;
|
||||
|
||||
namespace Sim.Host;
|
||||
|
||||
/// <summary>
|
||||
/// 项目资源(Properties.Resources)的素材库。平台为学生工程生成的 Properties/Resources.Designer.cs 通过这里读取素材,
|
||||
/// 和 VS 一样每次取图片都得到一个新的 Bitmap。
|
||||
/// </summary>
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public static class ProjectResources
|
||||
{
|
||||
/// <summary>素材文件所在目录(运行器目录下的 Resources/gallery)</summary>
|
||||
public static string Directory { get; } = Path.Combine(Path.GetDirectoryName(typeof(ProjectResources).Assembly.Location)!, "Resources", "gallery");
|
||||
|
||||
public static Bitmap Image(string file) => (Bitmap)System.Drawing.Image.FromStream(new MemoryStream(Bytes(file)));
|
||||
|
||||
public static byte[] Bytes(string file) => File.ReadAllBytes(Path.Combine(Directory, Path.GetFileName(file)));
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.Globalization;
|
||||
using System.Text.Json.Nodes;
|
||||
|
||||
namespace Sim.Host;
|
||||
|
||||
/// <summary>
|
||||
/// 虚拟时钟(实训台上可以拨时间、调倍速;教材项目 7 按时刻切换情景模式)。
|
||||
/// 编译时把学生代码里的 DateTime.Now / Today / UtcNow 改写成这里的属性(见 Sim.Runner/ClockRewriter.cs)。
|
||||
/// 没有拨过时就是真实时间。
|
||||
/// </summary>
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public static class SimClock
|
||||
{
|
||||
private sealed record Setting(DateTime Wall, long RealAt, double Speed);
|
||||
|
||||
private static volatile Setting? _setting;
|
||||
|
||||
public static DateTime Now
|
||||
{
|
||||
get
|
||||
{
|
||||
var setting = _setting;
|
||||
if (setting == null) return DateTime.Now;
|
||||
var elapsed = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds() - setting.RealAt;
|
||||
return setting.Wall.AddMilliseconds(elapsed * setting.Speed);
|
||||
}
|
||||
}
|
||||
|
||||
public static DateTime Today => Now.Date;
|
||||
|
||||
public static DateTime UtcNow => Now.ToUniversalTime();
|
||||
|
||||
/// <summary>平台内部使用:应用服务器推送的 {"wall":"2024-01-16T06:40:00","realAt":…,"speed":60},null 恢复真实时间</summary>
|
||||
public static void Update(JsonNode? clock)
|
||||
{
|
||||
if (clock is not JsonObject json
|
||||
|| !DateTime.TryParseExact((string?)json["wall"], "yyyy-MM-ddTHH:mm:ss", CultureInfo.InvariantCulture, DateTimeStyles.AssumeLocal, out var wall))
|
||||
{
|
||||
_setting = null;
|
||||
return;
|
||||
}
|
||||
_setting = new Setting(DateTime.SpecifyKind(wall, DateTimeKind.Local), (long?)json["realAt"] ?? 0, (double?)json["speed"] ?? 1);
|
||||
}
|
||||
}
|
||||
@@ -86,19 +86,23 @@ public static class SimHost
|
||||
public static string AppBaseDirectory { get; set; } = AppContext.BaseDirectory;
|
||||
|
||||
/// <summary>图片在浏览器端的编号;第一次用到时先把图片数据发过去,浏览器缓存后同一张图不再重发</summary>
|
||||
public static string ImageKey(System.Drawing.Image image)
|
||||
public static string ImageKey(System.Drawing.Image image) => DataKey(image.SimKey, image.Data, image.MimeType);
|
||||
|
||||
/// <summary>
|
||||
/// 图片、声音等数据在浏览器端的编号(key 通常是内容的哈希):第一次用到时先把数据发过去,之后只发编号。
|
||||
/// </summary>
|
||||
public static string DataKey(string key, byte[] data, string mime)
|
||||
{
|
||||
var key = image.SimKey;
|
||||
bool isNew;
|
||||
lock (SentImages) isNew = SentImages.Add(key);
|
||||
if (isNew)
|
||||
{
|
||||
Send(new JsonObject
|
||||
{
|
||||
["type"] = "image",
|
||||
["type"] = "blob",
|
||||
["key"] = key,
|
||||
["mime"] = image.MimeType,
|
||||
["data"] = Convert.ToBase64String(image.Data),
|
||||
["mime"] = mime,
|
||||
["data"] = Convert.ToBase64String(data),
|
||||
});
|
||||
}
|
||||
return key;
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
using System.ComponentModel;
|
||||
|
||||
namespace Sim.Host;
|
||||
|
||||
/// <summary>
|
||||
/// Windows 写法的路径(教材里常见 BaseDirectory + "Camera\\")在 Linux 上要把反斜杠换成斜杠,否则反斜杠成了文件名的一部分。
|
||||
/// 编译时把学生代码里传给文件、文件夹、图片读写方法的路径参数包上这个方法(见 Sim.Runner/PathRewriter.cs)。
|
||||
/// </summary>
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public static class WindowsPaths
|
||||
{
|
||||
public static string Fix(string path) => path?.Replace('\\', '/')!;
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
# 项目资源素材库
|
||||
|
||||
学生在"项目资源"里添加的图片和声音(`Properties.Resources.xxx`),名字和尺寸与教材一致,全部由平台自绘或合成(不使用教材配套资料里的素材)。
|
||||
|
||||
- `gallery/`:发布给运行器和网页的文件,`index.json` 是清单(名字、文件、中文说明、类型)。
|
||||
- `svg/`:图片的源文件;`project8.py` 生成项目 8 的那些 SVG。改了以后运行 `python3 project8.py && sh build-gallery.sh` 重新生成 PNG(需要 rsvg-convert 和思源黑体)。
|
||||
- `gallery/ring.mp3`:用 ffmpeg 合成的门铃声(叮—咚),命令见提交记录。
|
||||
@@ -0,0 +1,6 @@
|
||||
#!/bin/sh
|
||||
# 把 svg/ 下的素材渲染成 gallery/ 下的 PNG(尺寸取 SVG 自己的 width / height)。需要 rsvg-convert 和思源黑体等中文字体。
|
||||
cd "$(dirname "$0")"
|
||||
for f in svg/*.svg; do
|
||||
rsvg-convert "$f" -o "gallery/$(basename "$f" .svg).png"
|
||||
done
|
||||
|
After Width: | Height: | Size: 1.6 KiB |
|
After Width: | Height: | Size: 1.6 KiB |
|
After Width: | Height: | Size: 4.4 KiB |
|
After Width: | Height: | Size: 4.0 KiB |
|
After Width: | Height: | Size: 36 KiB |
|
After Width: | Height: | Size: 1.2 KiB |
|
After Width: | Height: | Size: 1.2 KiB |
|
After Width: | Height: | Size: 1.2 KiB |
|
After Width: | Height: | Size: 1.2 KiB |
|
After Width: | Height: | Size: 1.2 KiB |
|
After Width: | Height: | Size: 1.2 KiB |
|
After Width: | Height: | Size: 1.1 KiB |
|
After Width: | Height: | Size: 1.2 KiB |
|
After Width: | Height: | Size: 24 KiB |
|
After Width: | Height: | Size: 17 KiB |
|
After Width: | Height: | Size: 3.6 KiB |
|
After Width: | Height: | Size: 6.7 KiB |
|
After Width: | Height: | Size: 6.6 KiB |
|
After Width: | Height: | Size: 3.4 KiB |
|
After Width: | Height: | Size: 5.9 KiB |
@@ -0,0 +1,27 @@
|
||||
[
|
||||
{ "name": "switch_on", "file": "switch_on.png", "label": "开关:开", "kind": "image" },
|
||||
{ "name": "switch_off", "file": "switch_off.png", "label": "开关:关", "kind": "image" },
|
||||
{ "name": "ring", "file": "ring.mp3", "label": "铃声", "kind": "audio" },
|
||||
{ "name": "ico_title", "file": "ico_title.png", "label": "标题:智慧校园", "kind": "image" },
|
||||
{ "name": "bg_home", "file": "bg_home.png", "label": "主界面背景:校园", "kind": "image" },
|
||||
{ "name": "icon_02", "file": "icon_02.png", "label": "图标:校园环境", "kind": "image" },
|
||||
{ "name": "icon_03", "file": "icon_03.png", "label": "图标:校园安防", "kind": "image" },
|
||||
{ "name": "icon_04", "file": "icon_04.png", "label": "图标:退出", "kind": "image" },
|
||||
{ "name": "day_01", "file": "day_01.png", "label": "路灯场景:亮灯", "kind": "image" },
|
||||
{ "name": "day_02", "file": "day_02.png", "label": "路灯场景:灭灯", "kind": "image" },
|
||||
{ "name": "bg_from1", "file": "bg_from1.png", "label": "面板背景(宽)", "kind": "image" },
|
||||
{ "name": "bg_from2", "file": "bg_from2.png", "label": "面板背景(窄)", "kind": "image" },
|
||||
{ "name": "icon_monitor", "file": "icon_monitor.png", "label": "监控:无画面", "kind": "image" },
|
||||
{ "name": "panel_video_ctrl", "file": "panel_video_ctrl.png", "label": "监控:方向盘底座", "kind": "image" },
|
||||
{ "name": "btn_arrow_up_normal", "file": "btn_arrow_up_normal.png", "label": "方向键:上", "kind": "image" },
|
||||
{ "name": "btn_arrow_up_press", "file": "btn_arrow_up_press.png", "label": "方向键:上(按下)", "kind": "image" },
|
||||
{ "name": "btn_arrow_down_normal", "file": "btn_arrow_down_normal.png", "label": "方向键:下", "kind": "image" },
|
||||
{ "name": "btn_arrow_down_press", "file": "btn_arrow_down_press.png", "label": "方向键:下(按下)", "kind": "image" },
|
||||
{ "name": "btn_arrow_left_normal", "file": "btn_arrow_left_normal.png", "label": "方向键:左", "kind": "image" },
|
||||
{ "name": "btn_arrow_left_press", "file": "btn_arrow_left_press.png", "label": "方向键:左(按下)", "kind": "image" },
|
||||
{ "name": "btn_arrow_right_normal", "file": "btn_arrow_right_normal.png", "label": "方向键:右", "kind": "image" },
|
||||
{ "name": "btn_arrow_right_press", "file": "btn_arrow_right_press.png", "label": "方向键:右(按下)", "kind": "image" },
|
||||
{ "name": "arrow_left_normal", "file": "arrow_left_normal.png", "label": "翻页:上一张", "kind": "image" },
|
||||
{ "name": "arrow_right_normal", "file": "arrow_right_normal.png", "label": "翻页:下一张", "kind": "image" },
|
||||
{ "name": "windowstate_closed_hover", "file": "windowstate_closed_hover.png", "label": "关闭按钮", "kind": "image" }
|
||||
]
|
||||
|
After Width: | Height: | Size: 55 KiB |
|
After Width: | Height: | Size: 1.3 KiB |
|
After Width: | Height: | Size: 1.4 KiB |
|
After Width: | Height: | Size: 612 B |
@@ -0,0 +1,170 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
生成教材项目 8(智慧校园管理平台)用到的素材 SVG:名字和尺寸与教材配套资源一致,内容全部自绘(扁平插画)。
|
||||
运行后用 build-gallery.sh 渲染成 PNG:python3 project8.py && sh build-gallery.sh
|
||||
"""
|
||||
from pathlib import Path
|
||||
|
||||
OUT = Path(__file__).parent / "svg"
|
||||
OUT.mkdir(exist_ok=True)
|
||||
FONT = "Source Han Sans CN, Noto Sans CJK SC, sans-serif"
|
||||
|
||||
|
||||
def save(name, width, height, body):
|
||||
(OUT / f"{name}.svg").write_text(
|
||||
f'<svg xmlns="http://www.w3.org/2000/svg" width="{width}" height="{height}" viewBox="0 0 {width} {height}">\n{body}\n</svg>\n',
|
||||
encoding="utf-8",
|
||||
)
|
||||
|
||||
|
||||
# ---------- 标题图:智慧校园 ----------
|
||||
save("ico_title", 226, 54, f"""
|
||||
<g transform="translate(8,7)">
|
||||
<circle cx="20" cy="20" r="19" fill="#ffffff" opacity="0.95"/>
|
||||
<path d="M7 27 L20 10 L33 27 Z" fill="#3593ed"/>
|
||||
<rect x="13" y="22" width="14" height="9" fill="#3593ed"/>
|
||||
<rect x="18" y="25" width="4" height="6" fill="#ffffff"/>
|
||||
</g>
|
||||
<text x="56" y="37" font-family="{FONT}" font-size="27" font-weight="bold" fill="#ffffff" letter-spacing="3">智慧校园</text>
|
||||
""")
|
||||
|
||||
# ---------- 校园建筑:主界面背景、路灯场景共用的元素 ----------
|
||||
def building(x, y, w, h, color, window="#cfe3f5", rows=4, cols=6, roof="#b36b45"):
|
||||
parts = [f'<rect x="{x}" y="{y}" width="{w}" height="{h}" fill="{color}"/>',
|
||||
f'<rect x="{x-8}" y="{y-14}" width="{w+16}" height="16" fill="{roof}"/>']
|
||||
gap_x = w / cols
|
||||
gap_y = (h - 30) / rows
|
||||
for r in range(rows):
|
||||
for c in range(cols):
|
||||
parts.append(f'<rect x="{x + c*gap_x + gap_x*0.22:.1f}" y="{y + 18 + r*gap_y:.1f}" width="{gap_x*0.56:.1f}" height="{gap_y*0.55:.1f}" fill="{window}"/>')
|
||||
return "\n".join(parts)
|
||||
|
||||
|
||||
def tree(x, y, s=1.0, color="#4f9a45"):
|
||||
return (f'<rect x="{x-6*s}" y="{y}" width="{12*s}" height="{60*s}" fill="#7a5230"/>'
|
||||
f'<circle cx="{x}" cy="{y-30*s}" r="{48*s}" fill="{color}"/><circle cx="{x-32*s}" cy="{y-5*s}" r="{34*s}" fill="{color}"/>'
|
||||
f'<circle cx="{x+32*s}" cy="{y-5*s}" r="{34*s}" fill="#468c3e"/>')
|
||||
|
||||
|
||||
save("bg_home", 1440, 748, f"""
|
||||
<defs>
|
||||
<linearGradient id="sky" x1="0" y1="0" x2="0" y2="1"><stop offset="0" stop-color="#5aa7ea"/><stop offset="1" stop-color="#cfe8fb"/></linearGradient>
|
||||
<linearGradient id="lawn" x1="0" y1="0" x2="0" y2="1"><stop offset="0" stop-color="#8cc66d"/><stop offset="1" stop-color="#5f9e45"/></linearGradient>
|
||||
</defs>
|
||||
<rect width="1440" height="748" fill="url(#sky)"/>
|
||||
<circle cx="1260" cy="120" r="60" fill="#fff3b0" opacity="0.9"/>
|
||||
<g fill="#ffffff" opacity="0.85">
|
||||
<ellipse cx="260" cy="120" rx="90" ry="30"/><ellipse cx="320" cy="105" rx="60" ry="28"/>
|
||||
<ellipse cx="880" cy="90" rx="110" ry="32"/><ellipse cx="950" cy="80" rx="70" ry="28"/>
|
||||
</g>
|
||||
<g opacity="0.55" fill="#9fb8cf">
|
||||
<rect x="40" y="300" width="160" height="260"/><rect x="1230" y="280" width="180" height="280"/><rect x="230" y="340" width="120" height="220"/>
|
||||
</g>
|
||||
{building(380, 260, 680, 300, "#f2e3c9")}
|
||||
<rect x="660" y="190" width="120" height="70" fill="#f2e3c9"/><rect x="650" y="178" width="140" height="16" fill="#b36b45"/>
|
||||
<circle cx="720" cy="225" r="20" fill="#ffffff" stroke="#8a6a4a" stroke-width="4"/>
|
||||
<rect x="680" y="470" width="80" height="90" fill="#8a6a4a"/>
|
||||
{building(1080, 360, 220, 200, "#e8d6bd", rows=3, cols=4)}
|
||||
{building(150, 380, 210, 180, "#e8d6bd", rows=3, cols=4)}
|
||||
<rect x="0" y="560" width="1440" height="188" fill="url(#lawn)"/>
|
||||
<path d="M560 748 L680 560 L760 560 L880 748 Z" fill="#d9c9a8"/>
|
||||
{tree(120, 600, 1.1)}{tree(330, 620, 0.9)}{tree(1130, 610, 1.0)}{tree(1350, 600, 1.2)}
|
||||
<g fill="#3f8a3a"><ellipse cx="480" cy="572" rx="90" ry="22"/><ellipse cx="960" cy="572" rx="90" ry="22"/></g>
|
||||
""")
|
||||
|
||||
|
||||
def street(lit):
|
||||
sky = ('<stop offset="0" stop-color="#2b3d66"/><stop offset="1" stop-color="#e8a76b"/>' if lit
|
||||
else '<stop offset="0" stop-color="#6bb0ea"/><stop offset="1" stop-color="#d6ecfb"/>')
|
||||
window = "#ffd37a" if lit else "#cfe3f5"
|
||||
lamps = []
|
||||
for x in (180, 520, 860, 1200):
|
||||
lamps.append(f'<rect x="{x}" y="380" width="12" height="260" fill="#4b5563"/>'
|
||||
f'<path d="M{x+6} 386 Q{x+6} 356 {x+46} 356 H{x+70}" stroke="#4b5563" stroke-width="10" fill="none"/>'
|
||||
f'<rect x="{x+56}" y="348" width="44" height="16" rx="5" fill="#374151"/>')
|
||||
if lit:
|
||||
lamps.append(f'<path d="M{x+60} 364 L{x+20} 640 H{x+140} L{x+96} 364 Z" fill="#fff3b0" opacity="0.35"/>'
|
||||
f'<ellipse cx="{x+78}" cy="366" rx="22" ry="7" fill="#fff8d0"/>')
|
||||
else:
|
||||
lamps.append(f'<ellipse cx="{x+78}" cy="366" rx="18" ry="5" fill="#9ca3af"/>')
|
||||
return f"""
|
||||
<defs><linearGradient id="sky" x1="0" y1="0" x2="0" y2="1">{sky}</linearGradient></defs>
|
||||
<rect width="1440" height="716" fill="url(#sky)"/>
|
||||
{'<circle cx="1180" cy="110" r="36" fill="#fff6c8"/>' if lit else '<circle cx="1220" cy="110" r="50" fill="#fff3b0" opacity="0.9"/>'}
|
||||
{building(80, 220, 560, 330, "#e9d8bf", window=window, rows=5, cols=7)}
|
||||
{building(760, 260, 580, 290, "#dcc7a8", window=window, rows=4, cols=7)}
|
||||
<rect x="0" y="550" width="1440" height="40" fill="#7fb06a"/>
|
||||
<rect x="0" y="590" width="1440" height="126" fill="#5b6472"/>
|
||||
<g fill="#f8fafc">{''.join(f'<rect x="{x}" y="648" width="70" height="8"/>' for x in range(30, 1440, 140))}</g>
|
||||
{''.join(lamps)}
|
||||
{tree(700, 560, 0.8, "#3f7f45")}{tree(1390, 560, 0.9, "#3f7f45")}
|
||||
"""
|
||||
|
||||
|
||||
save("day_01", 1440, 716, street(lit=True))
|
||||
save("day_02", 1440, 716, street(lit=False))
|
||||
|
||||
# ---------- 主界面的三个功能图标 ----------
|
||||
def tile(name, color, icon, label):
|
||||
save(name, 180, 210, f"""
|
||||
<rect x="6" y="6" width="168" height="198" rx="18" fill="{color}"/>
|
||||
<rect x="6" y="6" width="168" height="198" rx="18" fill="#ffffff" opacity="0.08"/>
|
||||
<g transform="translate(90,82)" fill="#ffffff" stroke="#ffffff">{icon}</g>
|
||||
<text x="90" y="178" font-family="{FONT}" font-size="24" font-weight="bold" fill="#ffffff" text-anchor="middle">{label}</text>
|
||||
""")
|
||||
|
||||
|
||||
tile("icon_02", "#2e9e5b", """
|
||||
<rect x="-6" y="10" width="12" height="40" stroke="none"/>
|
||||
<circle cx="0" cy="-12" r="34" stroke="none"/><circle cx="-26" cy="8" r="22" stroke="none"/><circle cx="26" cy="8" r="22" stroke="none"/>
|
||||
<path d="M-4 -2 L0 -30 L4 -2" fill="#2e9e5b" stroke="none"/>""", "校园环境")
|
||||
tile("icon_03", "#2f7fe0", """
|
||||
<path d="M0 -48 L40 -32 V2 C40 28 20 44 0 52 C-20 44 -40 28 -40 2 V-32 Z" stroke="none"/>
|
||||
<path d="M-16 0 L-4 12 L18 -14" fill="none" stroke="#2f7fe0" stroke-width="9" stroke-linecap="round" stroke-linejoin="round"/>""", "校园安防")
|
||||
tile("icon_04", "#e2803a", """
|
||||
<path d="M-10 -44 H-40 V44 H-10" fill="none" stroke-width="10" stroke-linejoin="round"/>
|
||||
<path d="M-12 0 H40 M22 -18 L40 0 L22 18" fill="none" stroke-width="10" stroke-linecap="round" stroke-linejoin="round"/>""", "退 出")
|
||||
|
||||
# ---------- 面板背景(半透明深蓝) ----------
|
||||
for name, width in (("bg_from1", 555), ("bg_from2", 425)):
|
||||
save(name, width, 670, f"""
|
||||
<rect x="8" y="8" width="{width-16}" height="654" rx="12" fill="#14304f" opacity="0.88"/>
|
||||
<rect x="8" y="8" width="{width-16}" height="654" rx="12" fill="none" stroke="#7fb3e6" stroke-width="2" opacity="0.6"/>
|
||||
<rect x="24" y="22" width="{width-48}" height="4" rx="2" fill="#4fa3ff" opacity="0.8"/>
|
||||
""")
|
||||
|
||||
# ---------- 监控:无画面时的图标、方向盘底座、方向键 ----------
|
||||
save("icon_monitor", 280, 280, """
|
||||
<g fill="none" stroke="#c9ced6" stroke-width="10" stroke-linejoin="round" stroke-linecap="round">
|
||||
<rect x="40" y="70" width="160" height="110" rx="14"/>
|
||||
<path d="M200 105 L245 80 V170 L200 145"/>
|
||||
<path d="M90 180 L80 225 M150 180 L160 225 M65 225 H175"/>
|
||||
</g>
|
||||
<circle cx="120" cy="125" r="26" fill="none" stroke="#c9ced6" stroke-width="10"/>
|
||||
""")
|
||||
|
||||
save("panel_video_ctrl", 500, 500, """
|
||||
<defs><radialGradient id="pad" cx="0.5" cy="0.45" r="0.6"><stop offset="0" stop-color="#f4f6f9"/><stop offset="1" stop-color="#c7cdd6"/></radialGradient></defs>
|
||||
<circle cx="250" cy="250" r="240" fill="url(#pad)" stroke="#aab3bf" stroke-width="6"/>
|
||||
<circle cx="250" cy="250" r="95" fill="#e9edf2" stroke="#aab3bf" stroke-width="4"/>
|
||||
<circle cx="250" cy="250" r="40" fill="#d4dae2"/>
|
||||
""")
|
||||
|
||||
ROTATE = {"up": 0, "right": 90, "down": 180, "left": 270}
|
||||
for direction, angle in ROTATE.items():
|
||||
for state, (fill, stroke) in {"normal": ("#ffffff", "#5b6472"), "press": ("#2f7fe0", "#ffffff")}.items():
|
||||
save(f"btn_arrow_{direction}_{state}", 28, 28, f"""
|
||||
<circle cx="14" cy="14" r="13" fill="{fill}" stroke="#aab3bf" stroke-width="1"/>
|
||||
<path d="M8 16.5 L14 10 L20 16.5" fill="none" stroke="{stroke}" stroke-width="3" stroke-linecap="round" stroke-linejoin="round" transform="rotate({angle} 14 14)"/>
|
||||
""")
|
||||
|
||||
for direction, angle in (("left", 270), ("right", 90)):
|
||||
save(f"arrow_{direction}_normal", 60, 60, f"""
|
||||
<circle cx="30" cy="30" r="27" fill="#000000" opacity="0.45"/>
|
||||
<path d="M19 35 L30 23 L41 35" fill="none" stroke="#ffffff" stroke-width="5" stroke-linecap="round" stroke-linejoin="round" transform="rotate({angle} 30 30)"/>
|
||||
""")
|
||||
|
||||
save("windowstate_closed_hover", 36, 36, """
|
||||
<rect x="2" y="2" width="32" height="32" rx="4" fill="#e81123"/>
|
||||
<path d="M12 12 L24 24 M24 12 L12 24" stroke="#ffffff" stroke-width="3" stroke-linecap="round"/>
|
||||
""")
|
||||
@@ -0,0 +1,6 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="60" height="60" viewBox="0 0 60 60">
|
||||
|
||||
<circle cx="30" cy="30" r="27" fill="#000000" opacity="0.45"/>
|
||||
<path d="M19 35 L30 23 L41 35" fill="none" stroke="#ffffff" stroke-width="5" stroke-linecap="round" stroke-linejoin="round" transform="rotate(270 30 30)"/>
|
||||
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 316 B |
@@ -0,0 +1,6 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="60" height="60" viewBox="0 0 60 60">
|
||||
|
||||
<circle cx="30" cy="30" r="27" fill="#000000" opacity="0.45"/>
|
||||
<path d="M19 35 L30 23 L41 35" fill="none" stroke="#ffffff" stroke-width="5" stroke-linecap="round" stroke-linejoin="round" transform="rotate(90 30 30)"/>
|
||||
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 315 B |
@@ -0,0 +1,7 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="555" height="670" viewBox="0 0 555 670">
|
||||
|
||||
<rect x="8" y="8" width="539" height="654" rx="12" fill="#14304f" opacity="0.88"/>
|
||||
<rect x="8" y="8" width="539" height="654" rx="12" fill="none" stroke="#7fb3e6" stroke-width="2" opacity="0.6"/>
|
||||
<rect x="24" y="22" width="507" height="4" rx="2" fill="#4fa3ff" opacity="0.8"/>
|
||||
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 380 B |
@@ -0,0 +1,7 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="425" height="670" viewBox="0 0 425 670">
|
||||
|
||||
<rect x="8" y="8" width="409" height="654" rx="12" fill="#14304f" opacity="0.88"/>
|
||||
<rect x="8" y="8" width="409" height="654" rx="12" fill="none" stroke="#7fb3e6" stroke-width="2" opacity="0.6"/>
|
||||
<rect x="24" y="22" width="377" height="4" rx="2" fill="#4fa3ff" opacity="0.8"/>
|
||||
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 380 B |
@@ -0,0 +1,78 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="1440" height="748" viewBox="0 0 1440 748">
|
||||
|
||||
<defs>
|
||||
<linearGradient id="sky" x1="0" y1="0" x2="0" y2="1"><stop offset="0" stop-color="#5aa7ea"/><stop offset="1" stop-color="#cfe8fb"/></linearGradient>
|
||||
<linearGradient id="lawn" x1="0" y1="0" x2="0" y2="1"><stop offset="0" stop-color="#8cc66d"/><stop offset="1" stop-color="#5f9e45"/></linearGradient>
|
||||
</defs>
|
||||
<rect width="1440" height="748" fill="url(#sky)"/>
|
||||
<circle cx="1260" cy="120" r="60" fill="#fff3b0" opacity="0.9"/>
|
||||
<g fill="#ffffff" opacity="0.85">
|
||||
<ellipse cx="260" cy="120" rx="90" ry="30"/><ellipse cx="320" cy="105" rx="60" ry="28"/>
|
||||
<ellipse cx="880" cy="90" rx="110" ry="32"/><ellipse cx="950" cy="80" rx="70" ry="28"/>
|
||||
</g>
|
||||
<g opacity="0.55" fill="#9fb8cf">
|
||||
<rect x="40" y="300" width="160" height="260"/><rect x="1230" y="280" width="180" height="280"/><rect x="230" y="340" width="120" height="220"/>
|
||||
</g>
|
||||
<rect x="380" y="260" width="680" height="300" fill="#f2e3c9"/>
|
||||
<rect x="372" y="246" width="696" height="16" fill="#b36b45"/>
|
||||
<rect x="404.9" y="278.0" width="63.5" height="37.1" fill="#cfe3f5"/>
|
||||
<rect x="518.3" y="278.0" width="63.5" height="37.1" fill="#cfe3f5"/>
|
||||
<rect x="631.6" y="278.0" width="63.5" height="37.1" fill="#cfe3f5"/>
|
||||
<rect x="744.9" y="278.0" width="63.5" height="37.1" fill="#cfe3f5"/>
|
||||
<rect x="858.3" y="278.0" width="63.5" height="37.1" fill="#cfe3f5"/>
|
||||
<rect x="971.6" y="278.0" width="63.5" height="37.1" fill="#cfe3f5"/>
|
||||
<rect x="404.9" y="345.5" width="63.5" height="37.1" fill="#cfe3f5"/>
|
||||
<rect x="518.3" y="345.5" width="63.5" height="37.1" fill="#cfe3f5"/>
|
||||
<rect x="631.6" y="345.5" width="63.5" height="37.1" fill="#cfe3f5"/>
|
||||
<rect x="744.9" y="345.5" width="63.5" height="37.1" fill="#cfe3f5"/>
|
||||
<rect x="858.3" y="345.5" width="63.5" height="37.1" fill="#cfe3f5"/>
|
||||
<rect x="971.6" y="345.5" width="63.5" height="37.1" fill="#cfe3f5"/>
|
||||
<rect x="404.9" y="413.0" width="63.5" height="37.1" fill="#cfe3f5"/>
|
||||
<rect x="518.3" y="413.0" width="63.5" height="37.1" fill="#cfe3f5"/>
|
||||
<rect x="631.6" y="413.0" width="63.5" height="37.1" fill="#cfe3f5"/>
|
||||
<rect x="744.9" y="413.0" width="63.5" height="37.1" fill="#cfe3f5"/>
|
||||
<rect x="858.3" y="413.0" width="63.5" height="37.1" fill="#cfe3f5"/>
|
||||
<rect x="971.6" y="413.0" width="63.5" height="37.1" fill="#cfe3f5"/>
|
||||
<rect x="404.9" y="480.5" width="63.5" height="37.1" fill="#cfe3f5"/>
|
||||
<rect x="518.3" y="480.5" width="63.5" height="37.1" fill="#cfe3f5"/>
|
||||
<rect x="631.6" y="480.5" width="63.5" height="37.1" fill="#cfe3f5"/>
|
||||
<rect x="744.9" y="480.5" width="63.5" height="37.1" fill="#cfe3f5"/>
|
||||
<rect x="858.3" y="480.5" width="63.5" height="37.1" fill="#cfe3f5"/>
|
||||
<rect x="971.6" y="480.5" width="63.5" height="37.1" fill="#cfe3f5"/>
|
||||
<rect x="660" y="190" width="120" height="70" fill="#f2e3c9"/><rect x="650" y="178" width="140" height="16" fill="#b36b45"/>
|
||||
<circle cx="720" cy="225" r="20" fill="#ffffff" stroke="#8a6a4a" stroke-width="4"/>
|
||||
<rect x="680" y="470" width="80" height="90" fill="#8a6a4a"/>
|
||||
<rect x="1080" y="360" width="220" height="200" fill="#e8d6bd"/>
|
||||
<rect x="1072" y="346" width="236" height="16" fill="#b36b45"/>
|
||||
<rect x="1092.1" y="378.0" width="30.8" height="31.2" fill="#cfe3f5"/>
|
||||
<rect x="1147.1" y="378.0" width="30.8" height="31.2" fill="#cfe3f5"/>
|
||||
<rect x="1202.1" y="378.0" width="30.8" height="31.2" fill="#cfe3f5"/>
|
||||
<rect x="1257.1" y="378.0" width="30.8" height="31.2" fill="#cfe3f5"/>
|
||||
<rect x="1092.1" y="434.7" width="30.8" height="31.2" fill="#cfe3f5"/>
|
||||
<rect x="1147.1" y="434.7" width="30.8" height="31.2" fill="#cfe3f5"/>
|
||||
<rect x="1202.1" y="434.7" width="30.8" height="31.2" fill="#cfe3f5"/>
|
||||
<rect x="1257.1" y="434.7" width="30.8" height="31.2" fill="#cfe3f5"/>
|
||||
<rect x="1092.1" y="491.3" width="30.8" height="31.2" fill="#cfe3f5"/>
|
||||
<rect x="1147.1" y="491.3" width="30.8" height="31.2" fill="#cfe3f5"/>
|
||||
<rect x="1202.1" y="491.3" width="30.8" height="31.2" fill="#cfe3f5"/>
|
||||
<rect x="1257.1" y="491.3" width="30.8" height="31.2" fill="#cfe3f5"/>
|
||||
<rect x="150" y="380" width="210" height="180" fill="#e8d6bd"/>
|
||||
<rect x="142" y="366" width="226" height="16" fill="#b36b45"/>
|
||||
<rect x="161.6" y="398.0" width="29.4" height="27.5" fill="#cfe3f5"/>
|
||||
<rect x="214.1" y="398.0" width="29.4" height="27.5" fill="#cfe3f5"/>
|
||||
<rect x="266.6" y="398.0" width="29.4" height="27.5" fill="#cfe3f5"/>
|
||||
<rect x="319.1" y="398.0" width="29.4" height="27.5" fill="#cfe3f5"/>
|
||||
<rect x="161.6" y="448.0" width="29.4" height="27.5" fill="#cfe3f5"/>
|
||||
<rect x="214.1" y="448.0" width="29.4" height="27.5" fill="#cfe3f5"/>
|
||||
<rect x="266.6" y="448.0" width="29.4" height="27.5" fill="#cfe3f5"/>
|
||||
<rect x="319.1" y="448.0" width="29.4" height="27.5" fill="#cfe3f5"/>
|
||||
<rect x="161.6" y="498.0" width="29.4" height="27.5" fill="#cfe3f5"/>
|
||||
<rect x="214.1" y="498.0" width="29.4" height="27.5" fill="#cfe3f5"/>
|
||||
<rect x="266.6" y="498.0" width="29.4" height="27.5" fill="#cfe3f5"/>
|
||||
<rect x="319.1" y="498.0" width="29.4" height="27.5" fill="#cfe3f5"/>
|
||||
<rect x="0" y="560" width="1440" height="188" fill="url(#lawn)"/>
|
||||
<path d="M560 748 L680 560 L760 560 L880 748 Z" fill="#d9c9a8"/>
|
||||
<rect x="113.4" y="600" width="13.200000000000001" height="66.0" fill="#7a5230"/><circle cx="120" cy="567.0" r="52.800000000000004" fill="#4f9a45"/><circle cx="84.8" cy="594.5" r="37.400000000000006" fill="#4f9a45"/><circle cx="155.2" cy="594.5" r="37.400000000000006" fill="#468c3e"/><rect x="324.6" y="620" width="10.8" height="54.0" fill="#7a5230"/><circle cx="330" cy="593.0" r="43.2" fill="#4f9a45"/><circle cx="301.2" cy="615.5" r="30.6" fill="#4f9a45"/><circle cx="358.8" cy="615.5" r="30.6" fill="#468c3e"/><rect x="1124.0" y="610" width="12.0" height="60.0" fill="#7a5230"/><circle cx="1130" cy="580.0" r="48.0" fill="#4f9a45"/><circle cx="1098.0" cy="605.0" r="34.0" fill="#4f9a45"/><circle cx="1162.0" cy="605.0" r="34.0" fill="#468c3e"/><rect x="1342.8" y="600" width="14.399999999999999" height="72.0" fill="#7a5230"/><circle cx="1350" cy="564.0" r="57.599999999999994" fill="#4f9a45"/><circle cx="1311.6" cy="594.0" r="40.8" fill="#4f9a45"/><circle cx="1388.4" cy="594.0" r="40.8" fill="#468c3e"/>
|
||||
<g fill="#3f8a3a"><ellipse cx="480" cy="572" rx="90" ry="22"/><ellipse cx="960" cy="572" rx="90" ry="22"/></g>
|
||||
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 6.1 KiB |
@@ -0,0 +1,6 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="28" height="28" viewBox="0 0 28 28">
|
||||
|
||||
<circle cx="14" cy="14" r="13" fill="#ffffff" stroke="#aab3bf" stroke-width="1"/>
|
||||
<path d="M8 16.5 L14 10 L20 16.5" fill="none" stroke="#5b6472" stroke-width="3" stroke-linecap="round" stroke-linejoin="round" transform="rotate(180 14 14)"/>
|
||||
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 338 B |
@@ -0,0 +1,6 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="28" height="28" viewBox="0 0 28 28">
|
||||
|
||||
<circle cx="14" cy="14" r="13" fill="#2f7fe0" stroke="#aab3bf" stroke-width="1"/>
|
||||
<path d="M8 16.5 L14 10 L20 16.5" fill="none" stroke="#ffffff" stroke-width="3" stroke-linecap="round" stroke-linejoin="round" transform="rotate(180 14 14)"/>
|
||||
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 338 B |
@@ -0,0 +1,6 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="28" height="28" viewBox="0 0 28 28">
|
||||
|
||||
<circle cx="14" cy="14" r="13" fill="#ffffff" stroke="#aab3bf" stroke-width="1"/>
|
||||
<path d="M8 16.5 L14 10 L20 16.5" fill="none" stroke="#5b6472" stroke-width="3" stroke-linecap="round" stroke-linejoin="round" transform="rotate(270 14 14)"/>
|
||||
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 338 B |
@@ -0,0 +1,6 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="28" height="28" viewBox="0 0 28 28">
|
||||
|
||||
<circle cx="14" cy="14" r="13" fill="#2f7fe0" stroke="#aab3bf" stroke-width="1"/>
|
||||
<path d="M8 16.5 L14 10 L20 16.5" fill="none" stroke="#ffffff" stroke-width="3" stroke-linecap="round" stroke-linejoin="round" transform="rotate(270 14 14)"/>
|
||||
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 338 B |
@@ -0,0 +1,6 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="28" height="28" viewBox="0 0 28 28">
|
||||
|
||||
<circle cx="14" cy="14" r="13" fill="#ffffff" stroke="#aab3bf" stroke-width="1"/>
|
||||
<path d="M8 16.5 L14 10 L20 16.5" fill="none" stroke="#5b6472" stroke-width="3" stroke-linecap="round" stroke-linejoin="round" transform="rotate(90 14 14)"/>
|
||||
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 337 B |
@@ -0,0 +1,6 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="28" height="28" viewBox="0 0 28 28">
|
||||
|
||||
<circle cx="14" cy="14" r="13" fill="#2f7fe0" stroke="#aab3bf" stroke-width="1"/>
|
||||
<path d="M8 16.5 L14 10 L20 16.5" fill="none" stroke="#ffffff" stroke-width="3" stroke-linecap="round" stroke-linejoin="round" transform="rotate(90 14 14)"/>
|
||||
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 337 B |
@@ -0,0 +1,6 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="28" height="28" viewBox="0 0 28 28">
|
||||
|
||||
<circle cx="14" cy="14" r="13" fill="#ffffff" stroke="#aab3bf" stroke-width="1"/>
|
||||
<path d="M8 16.5 L14 10 L20 16.5" fill="none" stroke="#5b6472" stroke-width="3" stroke-linecap="round" stroke-linejoin="round" transform="rotate(0 14 14)"/>
|
||||
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 336 B |
@@ -0,0 +1,6 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="28" height="28" viewBox="0 0 28 28">
|
||||
|
||||
<circle cx="14" cy="14" r="13" fill="#2f7fe0" stroke="#aab3bf" stroke-width="1"/>
|
||||
<path d="M8 16.5 L14 10 L20 16.5" fill="none" stroke="#ffffff" stroke-width="3" stroke-linecap="round" stroke-linejoin="round" transform="rotate(0 14 14)"/>
|
||||
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 336 B |
@@ -0,0 +1,79 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="1440" height="716" viewBox="0 0 1440 716">
|
||||
|
||||
<defs><linearGradient id="sky" x1="0" y1="0" x2="0" y2="1"><stop offset="0" stop-color="#2b3d66"/><stop offset="1" stop-color="#e8a76b"/></linearGradient></defs>
|
||||
<rect width="1440" height="716" fill="url(#sky)"/>
|
||||
<circle cx="1180" cy="110" r="36" fill="#fff6c8"/>
|
||||
<rect x="80" y="220" width="560" height="330" fill="#e9d8bf"/>
|
||||
<rect x="72" y="206" width="576" height="16" fill="#b36b45"/>
|
||||
<rect x="97.6" y="238.0" width="44.8" height="33.0" fill="#ffd37a"/>
|
||||
<rect x="177.6" y="238.0" width="44.8" height="33.0" fill="#ffd37a"/>
|
||||
<rect x="257.6" y="238.0" width="44.8" height="33.0" fill="#ffd37a"/>
|
||||
<rect x="337.6" y="238.0" width="44.8" height="33.0" fill="#ffd37a"/>
|
||||
<rect x="417.6" y="238.0" width="44.8" height="33.0" fill="#ffd37a"/>
|
||||
<rect x="497.6" y="238.0" width="44.8" height="33.0" fill="#ffd37a"/>
|
||||
<rect x="577.6" y="238.0" width="44.8" height="33.0" fill="#ffd37a"/>
|
||||
<rect x="97.6" y="298.0" width="44.8" height="33.0" fill="#ffd37a"/>
|
||||
<rect x="177.6" y="298.0" width="44.8" height="33.0" fill="#ffd37a"/>
|
||||
<rect x="257.6" y="298.0" width="44.8" height="33.0" fill="#ffd37a"/>
|
||||
<rect x="337.6" y="298.0" width="44.8" height="33.0" fill="#ffd37a"/>
|
||||
<rect x="417.6" y="298.0" width="44.8" height="33.0" fill="#ffd37a"/>
|
||||
<rect x="497.6" y="298.0" width="44.8" height="33.0" fill="#ffd37a"/>
|
||||
<rect x="577.6" y="298.0" width="44.8" height="33.0" fill="#ffd37a"/>
|
||||
<rect x="97.6" y="358.0" width="44.8" height="33.0" fill="#ffd37a"/>
|
||||
<rect x="177.6" y="358.0" width="44.8" height="33.0" fill="#ffd37a"/>
|
||||
<rect x="257.6" y="358.0" width="44.8" height="33.0" fill="#ffd37a"/>
|
||||
<rect x="337.6" y="358.0" width="44.8" height="33.0" fill="#ffd37a"/>
|
||||
<rect x="417.6" y="358.0" width="44.8" height="33.0" fill="#ffd37a"/>
|
||||
<rect x="497.6" y="358.0" width="44.8" height="33.0" fill="#ffd37a"/>
|
||||
<rect x="577.6" y="358.0" width="44.8" height="33.0" fill="#ffd37a"/>
|
||||
<rect x="97.6" y="418.0" width="44.8" height="33.0" fill="#ffd37a"/>
|
||||
<rect x="177.6" y="418.0" width="44.8" height="33.0" fill="#ffd37a"/>
|
||||
<rect x="257.6" y="418.0" width="44.8" height="33.0" fill="#ffd37a"/>
|
||||
<rect x="337.6" y="418.0" width="44.8" height="33.0" fill="#ffd37a"/>
|
||||
<rect x="417.6" y="418.0" width="44.8" height="33.0" fill="#ffd37a"/>
|
||||
<rect x="497.6" y="418.0" width="44.8" height="33.0" fill="#ffd37a"/>
|
||||
<rect x="577.6" y="418.0" width="44.8" height="33.0" fill="#ffd37a"/>
|
||||
<rect x="97.6" y="478.0" width="44.8" height="33.0" fill="#ffd37a"/>
|
||||
<rect x="177.6" y="478.0" width="44.8" height="33.0" fill="#ffd37a"/>
|
||||
<rect x="257.6" y="478.0" width="44.8" height="33.0" fill="#ffd37a"/>
|
||||
<rect x="337.6" y="478.0" width="44.8" height="33.0" fill="#ffd37a"/>
|
||||
<rect x="417.6" y="478.0" width="44.8" height="33.0" fill="#ffd37a"/>
|
||||
<rect x="497.6" y="478.0" width="44.8" height="33.0" fill="#ffd37a"/>
|
||||
<rect x="577.6" y="478.0" width="44.8" height="33.0" fill="#ffd37a"/>
|
||||
<rect x="760" y="260" width="580" height="290" fill="#dcc7a8"/>
|
||||
<rect x="752" y="246" width="596" height="16" fill="#b36b45"/>
|
||||
<rect x="778.2" y="278.0" width="46.4" height="35.8" fill="#ffd37a"/>
|
||||
<rect x="861.1" y="278.0" width="46.4" height="35.8" fill="#ffd37a"/>
|
||||
<rect x="943.9" y="278.0" width="46.4" height="35.8" fill="#ffd37a"/>
|
||||
<rect x="1026.8" y="278.0" width="46.4" height="35.8" fill="#ffd37a"/>
|
||||
<rect x="1109.7" y="278.0" width="46.4" height="35.8" fill="#ffd37a"/>
|
||||
<rect x="1192.5" y="278.0" width="46.4" height="35.8" fill="#ffd37a"/>
|
||||
<rect x="1275.4" y="278.0" width="46.4" height="35.8" fill="#ffd37a"/>
|
||||
<rect x="778.2" y="343.0" width="46.4" height="35.8" fill="#ffd37a"/>
|
||||
<rect x="861.1" y="343.0" width="46.4" height="35.8" fill="#ffd37a"/>
|
||||
<rect x="943.9" y="343.0" width="46.4" height="35.8" fill="#ffd37a"/>
|
||||
<rect x="1026.8" y="343.0" width="46.4" height="35.8" fill="#ffd37a"/>
|
||||
<rect x="1109.7" y="343.0" width="46.4" height="35.8" fill="#ffd37a"/>
|
||||
<rect x="1192.5" y="343.0" width="46.4" height="35.8" fill="#ffd37a"/>
|
||||
<rect x="1275.4" y="343.0" width="46.4" height="35.8" fill="#ffd37a"/>
|
||||
<rect x="778.2" y="408.0" width="46.4" height="35.8" fill="#ffd37a"/>
|
||||
<rect x="861.1" y="408.0" width="46.4" height="35.8" fill="#ffd37a"/>
|
||||
<rect x="943.9" y="408.0" width="46.4" height="35.8" fill="#ffd37a"/>
|
||||
<rect x="1026.8" y="408.0" width="46.4" height="35.8" fill="#ffd37a"/>
|
||||
<rect x="1109.7" y="408.0" width="46.4" height="35.8" fill="#ffd37a"/>
|
||||
<rect x="1192.5" y="408.0" width="46.4" height="35.8" fill="#ffd37a"/>
|
||||
<rect x="1275.4" y="408.0" width="46.4" height="35.8" fill="#ffd37a"/>
|
||||
<rect x="778.2" y="473.0" width="46.4" height="35.8" fill="#ffd37a"/>
|
||||
<rect x="861.1" y="473.0" width="46.4" height="35.8" fill="#ffd37a"/>
|
||||
<rect x="943.9" y="473.0" width="46.4" height="35.8" fill="#ffd37a"/>
|
||||
<rect x="1026.8" y="473.0" width="46.4" height="35.8" fill="#ffd37a"/>
|
||||
<rect x="1109.7" y="473.0" width="46.4" height="35.8" fill="#ffd37a"/>
|
||||
<rect x="1192.5" y="473.0" width="46.4" height="35.8" fill="#ffd37a"/>
|
||||
<rect x="1275.4" y="473.0" width="46.4" height="35.8" fill="#ffd37a"/>
|
||||
<rect x="0" y="550" width="1440" height="40" fill="#7fb06a"/>
|
||||
<rect x="0" y="590" width="1440" height="126" fill="#5b6472"/>
|
||||
<g fill="#f8fafc"><rect x="30" y="648" width="70" height="8"/><rect x="170" y="648" width="70" height="8"/><rect x="310" y="648" width="70" height="8"/><rect x="450" y="648" width="70" height="8"/><rect x="590" y="648" width="70" height="8"/><rect x="730" y="648" width="70" height="8"/><rect x="870" y="648" width="70" height="8"/><rect x="1010" y="648" width="70" height="8"/><rect x="1150" y="648" width="70" height="8"/><rect x="1290" y="648" width="70" height="8"/><rect x="1430" y="648" width="70" height="8"/></g>
|
||||
<rect x="180" y="380" width="12" height="260" fill="#4b5563"/><path d="M186 386 Q186 356 226 356 H250" stroke="#4b5563" stroke-width="10" fill="none"/><rect x="236" y="348" width="44" height="16" rx="5" fill="#374151"/><path d="M240 364 L200 640 H320 L276 364 Z" fill="#fff3b0" opacity="0.35"/><ellipse cx="258" cy="366" rx="22" ry="7" fill="#fff8d0"/><rect x="520" y="380" width="12" height="260" fill="#4b5563"/><path d="M526 386 Q526 356 566 356 H590" stroke="#4b5563" stroke-width="10" fill="none"/><rect x="576" y="348" width="44" height="16" rx="5" fill="#374151"/><path d="M580 364 L540 640 H660 L616 364 Z" fill="#fff3b0" opacity="0.35"/><ellipse cx="598" cy="366" rx="22" ry="7" fill="#fff8d0"/><rect x="860" y="380" width="12" height="260" fill="#4b5563"/><path d="M866 386 Q866 356 906 356 H930" stroke="#4b5563" stroke-width="10" fill="none"/><rect x="916" y="348" width="44" height="16" rx="5" fill="#374151"/><path d="M920 364 L880 640 H1000 L956 364 Z" fill="#fff3b0" opacity="0.35"/><ellipse cx="938" cy="366" rx="22" ry="7" fill="#fff8d0"/><rect x="1200" y="380" width="12" height="260" fill="#4b5563"/><path d="M1206 386 Q1206 356 1246 356 H1270" stroke="#4b5563" stroke-width="10" fill="none"/><rect x="1256" y="348" width="44" height="16" rx="5" fill="#374151"/><path d="M1260 364 L1220 640 H1340 L1296 364 Z" fill="#fff3b0" opacity="0.35"/><ellipse cx="1278" cy="366" rx="22" ry="7" fill="#fff8d0"/>
|
||||
<rect x="695.2" y="560" width="9.600000000000001" height="48.0" fill="#7a5230"/><circle cx="700" cy="536.0" r="38.400000000000006" fill="#3f7f45"/><circle cx="674.4" cy="556.0" r="27.200000000000003" fill="#3f7f45"/><circle cx="725.6" cy="556.0" r="27.200000000000003" fill="#468c3e"/><rect x="1384.6" y="560" width="10.8" height="54.0" fill="#7a5230"/><circle cx="1390" cy="533.0" r="43.2" fill="#3f7f45"/><circle cx="1361.2" cy="555.5" r="30.6" fill="#3f7f45"/><circle cx="1418.8" cy="555.5" r="30.6" fill="#468c3e"/>
|
||||
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 7.5 KiB |
@@ -0,0 +1,79 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="1440" height="716" viewBox="0 0 1440 716">
|
||||
|
||||
<defs><linearGradient id="sky" x1="0" y1="0" x2="0" y2="1"><stop offset="0" stop-color="#6bb0ea"/><stop offset="1" stop-color="#d6ecfb"/></linearGradient></defs>
|
||||
<rect width="1440" height="716" fill="url(#sky)"/>
|
||||
<circle cx="1220" cy="110" r="50" fill="#fff3b0" opacity="0.9"/>
|
||||
<rect x="80" y="220" width="560" height="330" fill="#e9d8bf"/>
|
||||
<rect x="72" y="206" width="576" height="16" fill="#b36b45"/>
|
||||
<rect x="97.6" y="238.0" width="44.8" height="33.0" fill="#cfe3f5"/>
|
||||
<rect x="177.6" y="238.0" width="44.8" height="33.0" fill="#cfe3f5"/>
|
||||
<rect x="257.6" y="238.0" width="44.8" height="33.0" fill="#cfe3f5"/>
|
||||
<rect x="337.6" y="238.0" width="44.8" height="33.0" fill="#cfe3f5"/>
|
||||
<rect x="417.6" y="238.0" width="44.8" height="33.0" fill="#cfe3f5"/>
|
||||
<rect x="497.6" y="238.0" width="44.8" height="33.0" fill="#cfe3f5"/>
|
||||
<rect x="577.6" y="238.0" width="44.8" height="33.0" fill="#cfe3f5"/>
|
||||
<rect x="97.6" y="298.0" width="44.8" height="33.0" fill="#cfe3f5"/>
|
||||
<rect x="177.6" y="298.0" width="44.8" height="33.0" fill="#cfe3f5"/>
|
||||
<rect x="257.6" y="298.0" width="44.8" height="33.0" fill="#cfe3f5"/>
|
||||
<rect x="337.6" y="298.0" width="44.8" height="33.0" fill="#cfe3f5"/>
|
||||
<rect x="417.6" y="298.0" width="44.8" height="33.0" fill="#cfe3f5"/>
|
||||
<rect x="497.6" y="298.0" width="44.8" height="33.0" fill="#cfe3f5"/>
|
||||
<rect x="577.6" y="298.0" width="44.8" height="33.0" fill="#cfe3f5"/>
|
||||
<rect x="97.6" y="358.0" width="44.8" height="33.0" fill="#cfe3f5"/>
|
||||
<rect x="177.6" y="358.0" width="44.8" height="33.0" fill="#cfe3f5"/>
|
||||
<rect x="257.6" y="358.0" width="44.8" height="33.0" fill="#cfe3f5"/>
|
||||
<rect x="337.6" y="358.0" width="44.8" height="33.0" fill="#cfe3f5"/>
|
||||
<rect x="417.6" y="358.0" width="44.8" height="33.0" fill="#cfe3f5"/>
|
||||
<rect x="497.6" y="358.0" width="44.8" height="33.0" fill="#cfe3f5"/>
|
||||
<rect x="577.6" y="358.0" width="44.8" height="33.0" fill="#cfe3f5"/>
|
||||
<rect x="97.6" y="418.0" width="44.8" height="33.0" fill="#cfe3f5"/>
|
||||
<rect x="177.6" y="418.0" width="44.8" height="33.0" fill="#cfe3f5"/>
|
||||
<rect x="257.6" y="418.0" width="44.8" height="33.0" fill="#cfe3f5"/>
|
||||
<rect x="337.6" y="418.0" width="44.8" height="33.0" fill="#cfe3f5"/>
|
||||
<rect x="417.6" y="418.0" width="44.8" height="33.0" fill="#cfe3f5"/>
|
||||
<rect x="497.6" y="418.0" width="44.8" height="33.0" fill="#cfe3f5"/>
|
||||
<rect x="577.6" y="418.0" width="44.8" height="33.0" fill="#cfe3f5"/>
|
||||
<rect x="97.6" y="478.0" width="44.8" height="33.0" fill="#cfe3f5"/>
|
||||
<rect x="177.6" y="478.0" width="44.8" height="33.0" fill="#cfe3f5"/>
|
||||
<rect x="257.6" y="478.0" width="44.8" height="33.0" fill="#cfe3f5"/>
|
||||
<rect x="337.6" y="478.0" width="44.8" height="33.0" fill="#cfe3f5"/>
|
||||
<rect x="417.6" y="478.0" width="44.8" height="33.0" fill="#cfe3f5"/>
|
||||
<rect x="497.6" y="478.0" width="44.8" height="33.0" fill="#cfe3f5"/>
|
||||
<rect x="577.6" y="478.0" width="44.8" height="33.0" fill="#cfe3f5"/>
|
||||
<rect x="760" y="260" width="580" height="290" fill="#dcc7a8"/>
|
||||
<rect x="752" y="246" width="596" height="16" fill="#b36b45"/>
|
||||
<rect x="778.2" y="278.0" width="46.4" height="35.8" fill="#cfe3f5"/>
|
||||
<rect x="861.1" y="278.0" width="46.4" height="35.8" fill="#cfe3f5"/>
|
||||
<rect x="943.9" y="278.0" width="46.4" height="35.8" fill="#cfe3f5"/>
|
||||
<rect x="1026.8" y="278.0" width="46.4" height="35.8" fill="#cfe3f5"/>
|
||||
<rect x="1109.7" y="278.0" width="46.4" height="35.8" fill="#cfe3f5"/>
|
||||
<rect x="1192.5" y="278.0" width="46.4" height="35.8" fill="#cfe3f5"/>
|
||||
<rect x="1275.4" y="278.0" width="46.4" height="35.8" fill="#cfe3f5"/>
|
||||
<rect x="778.2" y="343.0" width="46.4" height="35.8" fill="#cfe3f5"/>
|
||||
<rect x="861.1" y="343.0" width="46.4" height="35.8" fill="#cfe3f5"/>
|
||||
<rect x="943.9" y="343.0" width="46.4" height="35.8" fill="#cfe3f5"/>
|
||||
<rect x="1026.8" y="343.0" width="46.4" height="35.8" fill="#cfe3f5"/>
|
||||
<rect x="1109.7" y="343.0" width="46.4" height="35.8" fill="#cfe3f5"/>
|
||||
<rect x="1192.5" y="343.0" width="46.4" height="35.8" fill="#cfe3f5"/>
|
||||
<rect x="1275.4" y="343.0" width="46.4" height="35.8" fill="#cfe3f5"/>
|
||||
<rect x="778.2" y="408.0" width="46.4" height="35.8" fill="#cfe3f5"/>
|
||||
<rect x="861.1" y="408.0" width="46.4" height="35.8" fill="#cfe3f5"/>
|
||||
<rect x="943.9" y="408.0" width="46.4" height="35.8" fill="#cfe3f5"/>
|
||||
<rect x="1026.8" y="408.0" width="46.4" height="35.8" fill="#cfe3f5"/>
|
||||
<rect x="1109.7" y="408.0" width="46.4" height="35.8" fill="#cfe3f5"/>
|
||||
<rect x="1192.5" y="408.0" width="46.4" height="35.8" fill="#cfe3f5"/>
|
||||
<rect x="1275.4" y="408.0" width="46.4" height="35.8" fill="#cfe3f5"/>
|
||||
<rect x="778.2" y="473.0" width="46.4" height="35.8" fill="#cfe3f5"/>
|
||||
<rect x="861.1" y="473.0" width="46.4" height="35.8" fill="#cfe3f5"/>
|
||||
<rect x="943.9" y="473.0" width="46.4" height="35.8" fill="#cfe3f5"/>
|
||||
<rect x="1026.8" y="473.0" width="46.4" height="35.8" fill="#cfe3f5"/>
|
||||
<rect x="1109.7" y="473.0" width="46.4" height="35.8" fill="#cfe3f5"/>
|
||||
<rect x="1192.5" y="473.0" width="46.4" height="35.8" fill="#cfe3f5"/>
|
||||
<rect x="1275.4" y="473.0" width="46.4" height="35.8" fill="#cfe3f5"/>
|
||||
<rect x="0" y="550" width="1440" height="40" fill="#7fb06a"/>
|
||||
<rect x="0" y="590" width="1440" height="126" fill="#5b6472"/>
|
||||
<g fill="#f8fafc"><rect x="30" y="648" width="70" height="8"/><rect x="170" y="648" width="70" height="8"/><rect x="310" y="648" width="70" height="8"/><rect x="450" y="648" width="70" height="8"/><rect x="590" y="648" width="70" height="8"/><rect x="730" y="648" width="70" height="8"/><rect x="870" y="648" width="70" height="8"/><rect x="1010" y="648" width="70" height="8"/><rect x="1150" y="648" width="70" height="8"/><rect x="1290" y="648" width="70" height="8"/><rect x="1430" y="648" width="70" height="8"/></g>
|
||||
<rect x="180" y="380" width="12" height="260" fill="#4b5563"/><path d="M186 386 Q186 356 226 356 H250" stroke="#4b5563" stroke-width="10" fill="none"/><rect x="236" y="348" width="44" height="16" rx="5" fill="#374151"/><ellipse cx="258" cy="366" rx="18" ry="5" fill="#9ca3af"/><rect x="520" y="380" width="12" height="260" fill="#4b5563"/><path d="M526 386 Q526 356 566 356 H590" stroke="#4b5563" stroke-width="10" fill="none"/><rect x="576" y="348" width="44" height="16" rx="5" fill="#374151"/><ellipse cx="598" cy="366" rx="18" ry="5" fill="#9ca3af"/><rect x="860" y="380" width="12" height="260" fill="#4b5563"/><path d="M866 386 Q866 356 906 356 H930" stroke="#4b5563" stroke-width="10" fill="none"/><rect x="916" y="348" width="44" height="16" rx="5" fill="#374151"/><ellipse cx="938" cy="366" rx="18" ry="5" fill="#9ca3af"/><rect x="1200" y="380" width="12" height="260" fill="#4b5563"/><path d="M1206 386 Q1206 356 1246 356 H1270" stroke="#4b5563" stroke-width="10" fill="none"/><rect x="1256" y="348" width="44" height="16" rx="5" fill="#374151"/><ellipse cx="1278" cy="366" rx="18" ry="5" fill="#9ca3af"/>
|
||||
<rect x="695.2" y="560" width="9.600000000000001" height="48.0" fill="#7a5230"/><circle cx="700" cy="536.0" r="38.400000000000006" fill="#3f7f45"/><circle cx="674.4" cy="556.0" r="27.200000000000003" fill="#3f7f45"/><circle cx="725.6" cy="556.0" r="27.200000000000003" fill="#468c3e"/><rect x="1384.6" y="560" width="10.8" height="54.0" fill="#7a5230"/><circle cx="1390" cy="533.0" r="43.2" fill="#3f7f45"/><circle cx="1361.2" cy="555.5" r="30.6" fill="#3f7f45"/><circle cx="1418.8" cy="555.5" r="30.6" fill="#468c3e"/>
|
||||
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 7.2 KiB |
@@ -0,0 +1,11 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="226" height="54" viewBox="0 0 226 54">
|
||||
|
||||
<g transform="translate(8,7)">
|
||||
<circle cx="20" cy="20" r="19" fill="#ffffff" opacity="0.95"/>
|
||||
<path d="M7 27 L20 10 L33 27 Z" fill="#3593ed"/>
|
||||
<rect x="13" y="22" width="14" height="9" fill="#3593ed"/>
|
||||
<rect x="18" y="25" width="4" height="6" fill="#ffffff"/>
|
||||
</g>
|
||||
<text x="56" y="37" font-family="Source Han Sans CN, Noto Sans CJK SC, sans-serif" font-size="27" font-weight="bold" fill="#ffffff" letter-spacing="3">智慧校园</text>
|
||||
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 553 B |
@@ -0,0 +1,11 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="180" height="210" viewBox="0 0 180 210">
|
||||
|
||||
<rect x="6" y="6" width="168" height="198" rx="18" fill="#2e9e5b"/>
|
||||
<rect x="6" y="6" width="168" height="198" rx="18" fill="#ffffff" opacity="0.08"/>
|
||||
<g transform="translate(90,82)" fill="#ffffff" stroke="#ffffff">
|
||||
<rect x="-6" y="10" width="12" height="40" stroke="none"/>
|
||||
<circle cx="0" cy="-12" r="34" stroke="none"/><circle cx="-26" cy="8" r="22" stroke="none"/><circle cx="26" cy="8" r="22" stroke="none"/>
|
||||
<path d="M-4 -2 L0 -30 L4 -2" fill="#2e9e5b" stroke="none"/></g>
|
||||
<text x="90" y="178" font-family="Source Han Sans CN, Noto Sans CJK SC, sans-serif" font-size="24" font-weight="bold" fill="#ffffff" text-anchor="middle">校园环境</text>
|
||||
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 769 B |
@@ -0,0 +1,10 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="180" height="210" viewBox="0 0 180 210">
|
||||
|
||||
<rect x="6" y="6" width="168" height="198" rx="18" fill="#2f7fe0"/>
|
||||
<rect x="6" y="6" width="168" height="198" rx="18" fill="#ffffff" opacity="0.08"/>
|
||||
<g transform="translate(90,82)" fill="#ffffff" stroke="#ffffff">
|
||||
<path d="M0 -48 L40 -32 V2 C40 28 20 44 0 52 C-20 44 -40 28 -40 2 V-32 Z" stroke="none"/>
|
||||
<path d="M-16 0 L-4 12 L18 -14" fill="none" stroke="#2f7fe0" stroke-width="9" stroke-linecap="round" stroke-linejoin="round"/></g>
|
||||
<text x="90" y="178" font-family="Source Han Sans CN, Noto Sans CJK SC, sans-serif" font-size="24" font-weight="bold" fill="#ffffff" text-anchor="middle">校园安防</text>
|
||||
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 724 B |
@@ -0,0 +1,10 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="180" height="210" viewBox="0 0 180 210">
|
||||
|
||||
<rect x="6" y="6" width="168" height="198" rx="18" fill="#e2803a"/>
|
||||
<rect x="6" y="6" width="168" height="198" rx="18" fill="#ffffff" opacity="0.08"/>
|
||||
<g transform="translate(90,82)" fill="#ffffff" stroke="#ffffff">
|
||||
<path d="M-10 -44 H-40 V44 H-10" fill="none" stroke-width="10" stroke-linejoin="round"/>
|
||||
<path d="M-12 0 H40 M22 -18 L40 0 L22 18" fill="none" stroke-width="10" stroke-linecap="round" stroke-linejoin="round"/></g>
|
||||
<text x="90" y="178" font-family="Source Han Sans CN, Noto Sans CJK SC, sans-serif" font-size="24" font-weight="bold" fill="#ffffff" text-anchor="middle">退 出</text>
|
||||
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 713 B |
@@ -0,0 +1,10 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="280" height="280" viewBox="0 0 280 280">
|
||||
|
||||
<g fill="none" stroke="#c9ced6" stroke-width="10" stroke-linejoin="round" stroke-linecap="round">
|
||||
<rect x="40" y="70" width="160" height="110" rx="14"/>
|
||||
<path d="M200 105 L245 80 V170 L200 145"/>
|
||||
<path d="M90 180 L80 225 M150 180 L160 225 M65 225 H175"/>
|
||||
</g>
|
||||
<circle cx="120" cy="125" r="26" fill="none" stroke="#c9ced6" stroke-width="10"/>
|
||||
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 457 B |
@@ -0,0 +1,8 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="500" height="500" viewBox="0 0 500 500">
|
||||
|
||||
<defs><radialGradient id="pad" cx="0.5" cy="0.45" r="0.6"><stop offset="0" stop-color="#f4f6f9"/><stop offset="1" stop-color="#c7cdd6"/></radialGradient></defs>
|
||||
<circle cx="250" cy="250" r="240" fill="url(#pad)" stroke="#aab3bf" stroke-width="6"/>
|
||||
<circle cx="250" cy="250" r="95" fill="#e9edf2" stroke="#aab3bf" stroke-width="4"/>
|
||||
<circle cx="250" cy="250" r="40" fill="#d4dae2"/>
|
||||
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 487 B |
@@ -0,0 +1,5 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="48" height="24" viewBox="0 0 48 24">
|
||||
<rect x="1" y="2" width="46" height="20" rx="10" fill="#c7cbd1" stroke="#a9aeb6" stroke-width="1"/>
|
||||
<text x="33" y="16.5" font-family="Arial, Helvetica, sans-serif" font-size="10" font-weight="bold" fill="#ffffff" text-anchor="middle">OFF</text>
|
||||
<circle cx="12" cy="12" r="8" fill="#ffffff"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 390 B |
@@ -0,0 +1,5 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="48" height="24" viewBox="0 0 48 24">
|
||||
<rect x="1" y="2" width="46" height="20" rx="10" fill="#34c759" stroke="#24a148" stroke-width="1"/>
|
||||
<text x="15" y="16.5" font-family="Arial, Helvetica, sans-serif" font-size="10" font-weight="bold" fill="#ffffff" text-anchor="middle">ON</text>
|
||||
<circle cx="36" cy="12" r="8" fill="#ffffff"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 389 B |
@@ -0,0 +1,6 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="36" height="36" viewBox="0 0 36 36">
|
||||
|
||||
<rect x="2" y="2" width="32" height="32" rx="4" fill="#e81123"/>
|
||||
<path d="M12 12 L24 24 M24 12 L12 24" stroke="#ffffff" stroke-width="3" stroke-linecap="round"/>
|
||||
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 259 B |
@@ -7,4 +7,8 @@
|
||||
<!-- 仿真类型的公开 API 要和真实 WinForms 一致,很多成员没有 XML 注释 -->
|
||||
<NoWarn>$(NoWarn);CS1591;CS8618</NoWarn>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<!-- 项目资源素材库(Properties.Resources 的图片、声音),随运行器一起输出 -->
|
||||
<None Include="Resources/gallery/*" CopyToOutputDirectory="PreserveNewest" LinkBase="Resources/gallery" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -0,0 +1,22 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace 教室情景模式
|
||||
{
|
||||
static class Program
|
||||
{
|
||||
/// <summary>
|
||||
/// 应用程序的主入口点。
|
||||
/// </summary>
|
||||
[STAThread]
|
||||
static void Main()
|
||||
{
|
||||
Application.EnableVisualStyles();
|
||||
Application.SetCompatibleTextRenderingDefault(false);
|
||||
Application.Run(new frm_control());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// <auto-generated>
|
||||
// 项目资源(平台素材库)。在"项目资源"里添加或移除,不要手动修改这个文件。
|
||||
// </auto-generated>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace 教室情景模式.Properties {
|
||||
|
||||
/// <summary>
|
||||
/// 项目资源:Properties.Resources.资源名
|
||||
/// </summary>
|
||||
internal class Resources {
|
||||
|
||||
/// <summary>开关:开</summary>
|
||||
internal static System.Drawing.Bitmap switch_on {
|
||||
get { return global::Sim.Host.ProjectResources.Image("switch_on.png"); }
|
||||
}
|
||||
|
||||
/// <summary>开关:关</summary>
|
||||
internal static System.Drawing.Bitmap switch_off {
|
||||
get { return global::Sim.Host.ProjectResources.Image("switch_off.png"); }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
# 评分规则:书上评价表的功能项。每项单独启动一次程序,按步骤操作,核对结果。
|
||||
# 格式说明见 server/src/grading/checks.ts
|
||||
- title: 能实时显示环境数据
|
||||
steps:
|
||||
- select: { control: com_import, item: COM2 }
|
||||
- click: btn_apply
|
||||
- expect: { control: btn_apply, text: 关闭, desc: 按钮文字变成"关闭" }
|
||||
- board: { light: 600, temperature: 22, humidity: 48 }
|
||||
- expect: { control: txt_light, text: '600', within: 5s }
|
||||
- expect: { control: txt_temperature, text: '22', within: 5s }
|
||||
- expect: { control: txt_humidity, text: '48', within: 5s }
|
||||
|
||||
- title: 能根据环境数据的变化实时控制智能设备
|
||||
steps:
|
||||
- select: { control: com_import, item: COM2 }
|
||||
- click: btn_apply
|
||||
- board: { light: 300, temperature: 22 }
|
||||
- expect: { relay: 1, on: true, within: 5s, desc: 光照低于 400 时打开照明灯 }
|
||||
- expect: { control: lamp, on: true, desc: 窗体上的灯亮 }
|
||||
- board: { light: 900 }
|
||||
- expect: { relay: 1, on: false, within: 5s, desc: 光照高于 800 时关闭照明灯 }
|
||||
- board: { temperature: 33 }
|
||||
- expect: { control: trb_airconditione, value: 26, within: 5s, desc: 温度高于 30℃ 时空调设为 26℃ }
|
||||
- board: { temperature: 3 }
|
||||
- expect: { control: trb_airconditione, value: 25, within: 5s, desc: 温度低于 5℃ 时空调设为 25℃ }
|
||||
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"title": "项目7 任务1 实现智慧教室",
|
||||
"source": "教材书 p133–137,界面和代码取自配套参考工程(手动开关灯的判断书上写反了,参考工程是对的);开关图片来自平台素材库"
|
||||
}
|
||||
@@ -0,0 +1,350 @@
|
||||
namespace 教室情景模式
|
||||
{
|
||||
partial class frm_control
|
||||
{
|
||||
/// <summary>
|
||||
/// 必需的设计器变量。
|
||||
/// </summary>
|
||||
private System.ComponentModel.IContainer components = null;
|
||||
|
||||
/// <summary>
|
||||
/// 清理所有正在使用的资源。
|
||||
/// </summary>
|
||||
/// <param name="disposing">如果应释放托管资源,为 true;否则为 false。</param>
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing && (components != null))
|
||||
{
|
||||
components.Dispose();
|
||||
}
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
#region Windows 窗体设计器生成的代码
|
||||
|
||||
/// <summary>
|
||||
/// 设计器支持所需的方法 - 不要
|
||||
/// 使用代码编辑器修改此方法的内容。
|
||||
/// </summary>
|
||||
private void InitializeComponent()
|
||||
{
|
||||
this.components = new System.ComponentModel.Container();
|
||||
this.lbl_mode = new System.Windows.Forms.Label();
|
||||
this.btn_apply = new System.Windows.Forms.Button();
|
||||
this.grp_mode = new System.Windows.Forms.GroupBox();
|
||||
this.grp_input = new System.Windows.Forms.GroupBox();
|
||||
this.txt_temperature = new System.Windows.Forms.TextBox();
|
||||
this.txt_humidity = new System.Windows.Forms.TextBox();
|
||||
this.com_import = new System.Windows.Forms.ComboBox();
|
||||
this.lbl_temperature = new System.Windows.Forms.Label();
|
||||
this.txt_light = new System.Windows.Forms.TextBox();
|
||||
this.lbl_import = new System.Windows.Forms.Label();
|
||||
this.lbl_humidity = new System.Windows.Forms.Label();
|
||||
this.lbl_light = new System.Windows.Forms.Label();
|
||||
this.grp_status = new System.Windows.Forms.GroupBox();
|
||||
this.pic_airconditioneStatus = new System.Windows.Forms.PictureBox();
|
||||
this.lbl_airconditionmin = new System.Windows.Forms.Label();
|
||||
this.lbl_airconditionmax = new System.Windows.Forms.Label();
|
||||
this.trb_airconditione = new System.Windows.Forms.TrackBar();
|
||||
this.lamp = new WinFormsDeviceControlLibrary.Lamp();
|
||||
this.lbl_airconditione = new System.Windows.Forms.Label();
|
||||
this.pic_lightsStatus = new System.Windows.Forms.PictureBox();
|
||||
this.lbl_lights = new System.Windows.Forms.Label();
|
||||
this.timer = new System.Windows.Forms.Timer(this.components);
|
||||
this.grp_mode.SuspendLayout();
|
||||
this.grp_input.SuspendLayout();
|
||||
this.grp_status.SuspendLayout();
|
||||
((System.ComponentModel.ISupportInitialize)(this.pic_airconditioneStatus)).BeginInit();
|
||||
((System.ComponentModel.ISupportInitialize)(this.trb_airconditione)).BeginInit();
|
||||
((System.ComponentModel.ISupportInitialize)(this.pic_lightsStatus)).BeginInit();
|
||||
this.SuspendLayout();
|
||||
//
|
||||
// lbl_mode
|
||||
//
|
||||
this.lbl_mode.AutoSize = true;
|
||||
this.lbl_mode.Location = new System.Drawing.Point(109, 30);
|
||||
this.lbl_mode.Margin = new System.Windows.Forms.Padding(2, 0, 2, 0);
|
||||
this.lbl_mode.Name = "lbl_mode";
|
||||
this.lbl_mode.Size = new System.Drawing.Size(89, 12);
|
||||
this.lbl_mode.TabIndex = 1;
|
||||
this.lbl_mode.Text = "智慧教室将开启";
|
||||
//
|
||||
// btn_apply
|
||||
//
|
||||
this.btn_apply.Location = new System.Drawing.Point(285, 22);
|
||||
this.btn_apply.Margin = new System.Windows.Forms.Padding(2);
|
||||
this.btn_apply.Name = "btn_apply";
|
||||
this.btn_apply.Size = new System.Drawing.Size(55, 28);
|
||||
this.btn_apply.TabIndex = 2;
|
||||
this.btn_apply.Text = "启用";
|
||||
this.btn_apply.UseVisualStyleBackColor = true;
|
||||
this.btn_apply.Click += new System.EventHandler(this.btn_apply_Click);
|
||||
//
|
||||
// grp_mode
|
||||
//
|
||||
this.grp_mode.Controls.Add(this.lbl_mode);
|
||||
this.grp_mode.Controls.Add(this.btn_apply);
|
||||
this.grp_mode.Location = new System.Drawing.Point(8, 8);
|
||||
this.grp_mode.Margin = new System.Windows.Forms.Padding(2);
|
||||
this.grp_mode.Name = "grp_mode";
|
||||
this.grp_mode.Padding = new System.Windows.Forms.Padding(2);
|
||||
this.grp_mode.Size = new System.Drawing.Size(396, 59);
|
||||
this.grp_mode.TabIndex = 3;
|
||||
this.grp_mode.TabStop = false;
|
||||
this.grp_mode.Text = "模式选择";
|
||||
//
|
||||
// grp_input
|
||||
//
|
||||
this.grp_input.Controls.Add(this.txt_temperature);
|
||||
this.grp_input.Controls.Add(this.txt_humidity);
|
||||
this.grp_input.Controls.Add(this.com_import);
|
||||
this.grp_input.Controls.Add(this.lbl_temperature);
|
||||
this.grp_input.Controls.Add(this.txt_light);
|
||||
this.grp_input.Controls.Add(this.lbl_import);
|
||||
this.grp_input.Controls.Add(this.lbl_humidity);
|
||||
this.grp_input.Controls.Add(this.lbl_light);
|
||||
this.grp_input.Location = new System.Drawing.Point(8, 71);
|
||||
this.grp_input.Margin = new System.Windows.Forms.Padding(2);
|
||||
this.grp_input.Name = "grp_input";
|
||||
this.grp_input.Padding = new System.Windows.Forms.Padding(2);
|
||||
this.grp_input.Size = new System.Drawing.Size(133, 236);
|
||||
this.grp_input.TabIndex = 4;
|
||||
this.grp_input.TabStop = false;
|
||||
this.grp_input.Text = "采集量";
|
||||
//
|
||||
// txt_temperature
|
||||
//
|
||||
this.txt_temperature.Location = new System.Drawing.Point(56, 113);
|
||||
this.txt_temperature.Margin = new System.Windows.Forms.Padding(2);
|
||||
this.txt_temperature.Name = "txt_temperature";
|
||||
this.txt_temperature.Size = new System.Drawing.Size(65, 21);
|
||||
this.txt_temperature.TabIndex = 3;
|
||||
//
|
||||
// txt_humidity
|
||||
//
|
||||
this.txt_humidity.Location = new System.Drawing.Point(57, 160);
|
||||
this.txt_humidity.Margin = new System.Windows.Forms.Padding(2);
|
||||
this.txt_humidity.Name = "txt_humidity";
|
||||
this.txt_humidity.Size = new System.Drawing.Size(65, 21);
|
||||
this.txt_humidity.TabIndex = 3;
|
||||
//
|
||||
// com_import
|
||||
//
|
||||
this.com_import.FormattingEnabled = true;
|
||||
this.com_import.Location = new System.Drawing.Point(57, 20);
|
||||
this.com_import.Margin = new System.Windows.Forms.Padding(2);
|
||||
this.com_import.Name = "com_import";
|
||||
this.com_import.Size = new System.Drawing.Size(65, 20);
|
||||
this.com_import.TabIndex = 6;
|
||||
//
|
||||
// lbl_temperature
|
||||
//
|
||||
this.lbl_temperature.AutoSize = true;
|
||||
this.lbl_temperature.Font = new System.Drawing.Font("宋体", 9F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(134)));
|
||||
this.lbl_temperature.Location = new System.Drawing.Point(22, 117);
|
||||
this.lbl_temperature.Margin = new System.Windows.Forms.Padding(2, 0, 2, 0);
|
||||
this.lbl_temperature.Name = "lbl_temperature";
|
||||
this.lbl_temperature.Size = new System.Drawing.Size(31, 12);
|
||||
this.lbl_temperature.TabIndex = 1;
|
||||
this.lbl_temperature.Text = "温度";
|
||||
//
|
||||
// txt_light
|
||||
//
|
||||
this.txt_light.Location = new System.Drawing.Point(57, 67);
|
||||
this.txt_light.Margin = new System.Windows.Forms.Padding(2);
|
||||
this.txt_light.Name = "txt_light";
|
||||
this.txt_light.Size = new System.Drawing.Size(65, 21);
|
||||
this.txt_light.TabIndex = 2;
|
||||
//
|
||||
// lbl_import
|
||||
//
|
||||
this.lbl_import.AutoSize = true;
|
||||
this.lbl_import.Location = new System.Drawing.Point(23, 23);
|
||||
this.lbl_import.Margin = new System.Windows.Forms.Padding(2, 0, 2, 0);
|
||||
this.lbl_import.Name = "lbl_import";
|
||||
this.lbl_import.Size = new System.Drawing.Size(29, 12);
|
||||
this.lbl_import.TabIndex = 1;
|
||||
this.lbl_import.Text = "串口";
|
||||
//
|
||||
// lbl_humidity
|
||||
//
|
||||
this.lbl_humidity.AutoSize = true;
|
||||
this.lbl_humidity.Font = new System.Drawing.Font("宋体", 9F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(134)));
|
||||
this.lbl_humidity.Location = new System.Drawing.Point(23, 163);
|
||||
this.lbl_humidity.Margin = new System.Windows.Forms.Padding(2, 0, 2, 0);
|
||||
this.lbl_humidity.Name = "lbl_humidity";
|
||||
this.lbl_humidity.Size = new System.Drawing.Size(31, 12);
|
||||
this.lbl_humidity.TabIndex = 1;
|
||||
this.lbl_humidity.Text = "湿度";
|
||||
//
|
||||
// lbl_light
|
||||
//
|
||||
this.lbl_light.AutoSize = true;
|
||||
this.lbl_light.Font = new System.Drawing.Font("宋体", 9F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(134)));
|
||||
this.lbl_light.Location = new System.Drawing.Point(11, 70);
|
||||
this.lbl_light.Margin = new System.Windows.Forms.Padding(2, 0, 2, 0);
|
||||
this.lbl_light.Name = "lbl_light";
|
||||
this.lbl_light.Size = new System.Drawing.Size(44, 12);
|
||||
this.lbl_light.TabIndex = 0;
|
||||
this.lbl_light.Text = "光照度";
|
||||
//
|
||||
// grp_status
|
||||
//
|
||||
this.grp_status.Controls.Add(this.pic_airconditioneStatus);
|
||||
this.grp_status.Controls.Add(this.lbl_airconditionmin);
|
||||
this.grp_status.Controls.Add(this.lbl_airconditionmax);
|
||||
this.grp_status.Controls.Add(this.trb_airconditione);
|
||||
this.grp_status.Controls.Add(this.lamp);
|
||||
this.grp_status.Controls.Add(this.lbl_airconditione);
|
||||
this.grp_status.Controls.Add(this.pic_lightsStatus);
|
||||
this.grp_status.Controls.Add(this.lbl_lights);
|
||||
this.grp_status.Location = new System.Drawing.Point(145, 71);
|
||||
this.grp_status.Margin = new System.Windows.Forms.Padding(2);
|
||||
this.grp_status.Name = "grp_status";
|
||||
this.grp_status.Padding = new System.Windows.Forms.Padding(2);
|
||||
this.grp_status.Size = new System.Drawing.Size(259, 236);
|
||||
this.grp_status.TabIndex = 5;
|
||||
this.grp_status.TabStop = false;
|
||||
this.grp_status.Text = "状态栏";
|
||||
//
|
||||
// pic_airconditioneStatus
|
||||
//
|
||||
this.pic_airconditioneStatus.Image = global::教室情景模式.Properties.Resources.switch_off;
|
||||
this.pic_airconditioneStatus.Location = new System.Drawing.Point(58, 27);
|
||||
this.pic_airconditioneStatus.Margin = new System.Windows.Forms.Padding(2);
|
||||
this.pic_airconditioneStatus.Name = "pic_airconditioneStatus";
|
||||
this.pic_airconditioneStatus.Size = new System.Drawing.Size(33, 17);
|
||||
this.pic_airconditioneStatus.SizeMode = System.Windows.Forms.PictureBoxSizeMode.Zoom;
|
||||
this.pic_airconditioneStatus.TabIndex = 6;
|
||||
this.pic_airconditioneStatus.TabStop = false;
|
||||
//
|
||||
// lbl_airconditionmin
|
||||
//
|
||||
this.lbl_airconditionmin.AutoSize = true;
|
||||
this.lbl_airconditionmin.Location = new System.Drawing.Point(10, 87);
|
||||
this.lbl_airconditionmin.Margin = new System.Windows.Forms.Padding(2, 0, 2, 0);
|
||||
this.lbl_airconditionmin.Name = "lbl_airconditionmin";
|
||||
this.lbl_airconditionmin.Size = new System.Drawing.Size(29, 12);
|
||||
this.lbl_airconditionmin.TabIndex = 5;
|
||||
this.lbl_airconditionmin.Text = "18℃";
|
||||
//
|
||||
// lbl_airconditionmax
|
||||
//
|
||||
this.lbl_airconditionmax.AutoSize = true;
|
||||
this.lbl_airconditionmax.Location = new System.Drawing.Point(95, 87);
|
||||
this.lbl_airconditionmax.Margin = new System.Windows.Forms.Padding(2, 0, 2, 0);
|
||||
this.lbl_airconditionmax.Name = "lbl_airconditionmax";
|
||||
this.lbl_airconditionmax.Size = new System.Drawing.Size(29, 12);
|
||||
this.lbl_airconditionmax.TabIndex = 5;
|
||||
this.lbl_airconditionmax.Text = "32℃";
|
||||
//
|
||||
// trb_airconditione
|
||||
//
|
||||
this.trb_airconditione.Location = new System.Drawing.Point(12, 53);
|
||||
this.trb_airconditione.Margin = new System.Windows.Forms.Padding(2);
|
||||
this.trb_airconditione.Maximum = 32;
|
||||
this.trb_airconditione.Minimum = 18;
|
||||
this.trb_airconditione.Name = "trb_airconditione";
|
||||
this.trb_airconditione.Size = new System.Drawing.Size(107, 45);
|
||||
this.trb_airconditione.TabIndex = 4;
|
||||
this.trb_airconditione.Value = 18;
|
||||
//
|
||||
// lamp
|
||||
//
|
||||
this.lamp.BackColor = System.Drawing.Color.Transparent;
|
||||
this.lamp.Location = new System.Drawing.Point(139, 40);
|
||||
this.lamp.Name = "lamp";
|
||||
this.lamp.Size = new System.Drawing.Size(105, 180);
|
||||
this.lamp.TabIndex = 3;
|
||||
//
|
||||
// lbl_airconditione
|
||||
//
|
||||
this.lbl_airconditione.AutoSize = true;
|
||||
this.lbl_airconditione.Font = new System.Drawing.Font("宋体", 9F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(134)));
|
||||
this.lbl_airconditione.Location = new System.Drawing.Point(10, 27);
|
||||
this.lbl_airconditione.Margin = new System.Windows.Forms.Padding(2, 0, 2, 0);
|
||||
this.lbl_airconditione.Name = "lbl_airconditione";
|
||||
this.lbl_airconditione.Size = new System.Drawing.Size(31, 12);
|
||||
this.lbl_airconditione.TabIndex = 2;
|
||||
this.lbl_airconditione.Text = "空调";
|
||||
//
|
||||
// pic_lightsStatus
|
||||
//
|
||||
this.pic_lightsStatus.Image = global::教室情景模式.Properties.Resources.switch_off;
|
||||
this.pic_lightsStatus.Location = new System.Drawing.Point(204, 19);
|
||||
this.pic_lightsStatus.Margin = new System.Windows.Forms.Padding(2);
|
||||
this.pic_lightsStatus.Name = "pic_lightsStatus";
|
||||
this.pic_lightsStatus.Size = new System.Drawing.Size(33, 17);
|
||||
this.pic_lightsStatus.SizeMode = System.Windows.Forms.PictureBoxSizeMode.Zoom;
|
||||
this.pic_lightsStatus.TabIndex = 1;
|
||||
this.pic_lightsStatus.TabStop = false;
|
||||
this.pic_lightsStatus.Click += new System.EventHandler(this.pic_lightsStatus_Click);
|
||||
//
|
||||
// lbl_lights
|
||||
//
|
||||
this.lbl_lights.AutoSize = true;
|
||||
this.lbl_lights.Font = new System.Drawing.Font("宋体", 9F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(134)));
|
||||
this.lbl_lights.Location = new System.Drawing.Point(161, 19);
|
||||
this.lbl_lights.Margin = new System.Windows.Forms.Padding(2, 0, 2, 0);
|
||||
this.lbl_lights.Name = "lbl_lights";
|
||||
this.lbl_lights.Size = new System.Drawing.Size(31, 12);
|
||||
this.lbl_lights.TabIndex = 0;
|
||||
this.lbl_lights.Text = "灯光";
|
||||
//
|
||||
// timer
|
||||
//
|
||||
this.timer.Interval = 2000;
|
||||
this.timer.Tick += new System.EventHandler(this.timer_Tick);
|
||||
//
|
||||
// frm_control
|
||||
//
|
||||
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 12F);
|
||||
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
||||
this.ClientSize = new System.Drawing.Size(417, 315);
|
||||
this.Controls.Add(this.grp_status);
|
||||
this.Controls.Add(this.grp_input);
|
||||
this.Controls.Add(this.grp_mode);
|
||||
this.Margin = new System.Windows.Forms.Padding(2);
|
||||
this.Name = "frm_control";
|
||||
this.Text = "智慧教室情景控制";
|
||||
this.Load += new System.EventHandler(this.frm_control_Load);
|
||||
this.grp_mode.ResumeLayout(false);
|
||||
this.grp_mode.PerformLayout();
|
||||
this.grp_input.ResumeLayout(false);
|
||||
this.grp_input.PerformLayout();
|
||||
this.grp_status.ResumeLayout(false);
|
||||
this.grp_status.PerformLayout();
|
||||
((System.ComponentModel.ISupportInitialize)(this.pic_airconditioneStatus)).EndInit();
|
||||
((System.ComponentModel.ISupportInitialize)(this.trb_airconditione)).EndInit();
|
||||
((System.ComponentModel.ISupportInitialize)(this.pic_lightsStatus)).EndInit();
|
||||
this.ResumeLayout(false);
|
||||
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private System.Windows.Forms.Label lbl_mode;
|
||||
private System.Windows.Forms.Button btn_apply;
|
||||
private System.Windows.Forms.GroupBox grp_mode;
|
||||
private System.Windows.Forms.GroupBox grp_input;
|
||||
private System.Windows.Forms.TextBox txt_humidity;
|
||||
private System.Windows.Forms.TextBox txt_light;
|
||||
private System.Windows.Forms.Label lbl_humidity;
|
||||
private System.Windows.Forms.Label lbl_light;
|
||||
private System.Windows.Forms.GroupBox grp_status;
|
||||
private System.Windows.Forms.Label lbl_lights;
|
||||
private System.Windows.Forms.TextBox txt_temperature;
|
||||
private System.Windows.Forms.Label lbl_temperature;
|
||||
private System.Windows.Forms.ComboBox com_import;
|
||||
private System.Windows.Forms.Label lbl_import;
|
||||
private System.Windows.Forms.Timer timer;
|
||||
private System.Windows.Forms.PictureBox pic_lightsStatus;
|
||||
private WinFormsDeviceControlLibrary.Lamp lamp;
|
||||
private System.Windows.Forms.Label lbl_airconditionmin;
|
||||
private System.Windows.Forms.Label lbl_airconditionmax;
|
||||
private System.Windows.Forms.TrackBar trb_airconditione;
|
||||
private System.Windows.Forms.Label lbl_airconditione;
|
||||
private System.Windows.Forms.PictureBox pic_airconditioneStatus;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,126 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Data;
|
||||
using System.Drawing;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Forms;
|
||||
using System.IO.Ports;//引用系统IO端口库
|
||||
using DeviceCtrlLibrary;//引用自定义设备控制库
|
||||
using DeviceCtrlLibrary.Model;//引用自定义设备控制模型库
|
||||
|
||||
namespace 教室情景模式
|
||||
{
|
||||
public partial class frm_control : Form
|
||||
{
|
||||
bool IsOpen = false;//定义布尔变量记录串口打开情况,默认为false
|
||||
bool IsLampOpen = false;//定义布尔变量记录电灯打开情况,默认为false
|
||||
bool IsairconditioneOpen = false;//定义布尔变量记录空调打开情况,默认为false
|
||||
ADAM4017ResModel ResModel;//创建4017接收数据模型
|
||||
|
||||
public frm_control()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
private void frm_control_Load(object sender, EventArgs e)
|
||||
{
|
||||
//将系统串口名依次加入下拉列表框中
|
||||
foreach (string item in SerialPort.GetPortNames())
|
||||
{
|
||||
com_import.Items.Add(item);
|
||||
}
|
||||
}
|
||||
|
||||
private void btn_apply_Click(object sender, EventArgs e)
|
||||
{
|
||||
if (!IsOpen)//判断串口是否打开
|
||||
{
|
||||
DeviceCtrlHelper.adam4017Helper.Open(com_import.Text);//打开串口
|
||||
DeviceCtrlHelper.adam4150Helper.Open(com_import.Text);//打开串口
|
||||
btn_apply.Text = "关闭";//按钮btn_apply显示“关闭”
|
||||
lbl_mode.Text = "智慧教室已开启";//标签lbl_mode显示“智慧教室已开启”
|
||||
IsOpen = true;
|
||||
timer.Start();//通过timer_Tick实时把采集到的数据显示出来,并判断是否需要开灯、关窗帘
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
DeviceCtrlHelper.adam4017Helper.Close();//关闭采集
|
||||
DeviceCtrlHelper.adam4150Helper.Close();//关闭4150
|
||||
timer.Stop();//停止数据采集与自动控制
|
||||
btn_apply.Text = "启用";
|
||||
lbl_mode.Text = "智慧教室将开启";
|
||||
IsOpen = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
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 (Convert.ToInt16(ResModel.lightValue) < 400)//光照度过低则打开灯
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
private void pic_lightsStatus_Click(object sender, EventArgs e)
|
||||
{
|
||||
if (IsLampOpen)
|
||||
{
|
||||
pic_lightsStatus.Image = Properties.Resources.switch_off;
|
||||
IsLampOpen = false;
|
||||
lamp.OnOff(false);
|
||||
DeviceCtrlHelper.adam4150Helper.OnOff(1, false);//关闭1通道灯
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
pic_lightsStatus.Image = Properties.Resources.switch_on;
|
||||
IsLampOpen = true;
|
||||
lamp.OnOff(true);
|
||||
DeviceCtrlHelper.adam4150Helper.OnOff(1, true);//打开1通道灯
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,388 @@
|
||||
namespace 寝室情景模式
|
||||
{
|
||||
partial class frm_control
|
||||
{
|
||||
/// <summary>
|
||||
/// 必需的设计器变量。
|
||||
/// </summary>
|
||||
private System.ComponentModel.IContainer components = null;
|
||||
|
||||
/// <summary>
|
||||
/// 清理所有正在使用的资源。
|
||||
/// </summary>
|
||||
/// <param name="disposing">如果应释放托管资源,为 true;否则为 false。</param>
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing && (components != null))
|
||||
{
|
||||
components.Dispose();
|
||||
}
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
#region Windows 窗体设计器生成的代码
|
||||
|
||||
/// <summary>
|
||||
/// 设计器支持所需的方法 - 不要
|
||||
/// 使用代码编辑器修改此方法的内容。
|
||||
/// </summary>
|
||||
private void InitializeComponent()
|
||||
{
|
||||
this.components = new System.ComponentModel.Container();
|
||||
System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(frm_control));
|
||||
this.grp_status = new System.Windows.Forms.GroupBox();
|
||||
this.alarm1 = new WinFormsDeviceControlLibrary.Alarm();
|
||||
this.lamp = new WinFormsDeviceControlLibrary.Lamp();
|
||||
this.lbl_windowopen = new System.Windows.Forms.Label();
|
||||
this.lbl_windowclose = new System.Windows.Forms.Label();
|
||||
this.trb_curtain = new System.Windows.Forms.TrackBar();
|
||||
this.lbl_boardcast = new System.Windows.Forms.Label();
|
||||
this.lbl_curtain = new System.Windows.Forms.Label();
|
||||
this.pic_boardcastStatus = new System.Windows.Forms.PictureBox();
|
||||
this.lbl_lights = new System.Windows.Forms.Label();
|
||||
this.grp_input = new System.Windows.Forms.GroupBox();
|
||||
this.txt_temperature = new System.Windows.Forms.TextBox();
|
||||
this.txt_humidity = new System.Windows.Forms.TextBox();
|
||||
this.com_import = new System.Windows.Forms.ComboBox();
|
||||
this.lbl_temperature = new System.Windows.Forms.Label();
|
||||
this.txt_light = new System.Windows.Forms.TextBox();
|
||||
this.lbl_import = new System.Windows.Forms.Label();
|
||||
this.lbl_humidity = new System.Windows.Forms.Label();
|
||||
this.lbl_light = new System.Windows.Forms.Label();
|
||||
this.grp_mode = new System.Windows.Forms.GroupBox();
|
||||
this.lbl_time = new System.Windows.Forms.Label();
|
||||
this.txt_time = new System.Windows.Forms.TextBox();
|
||||
this.lbl_mode = new System.Windows.Forms.Label();
|
||||
this.btn_apply = new System.Windows.Forms.Button();
|
||||
this.timer = new System.Windows.Forms.Timer(this.components);
|
||||
this.axWindowsMediaPlayer1 = new AxWMPLib.AxWindowsMediaPlayer();
|
||||
this.grp_status.SuspendLayout();
|
||||
((System.ComponentModel.ISupportInitialize)(this.trb_curtain)).BeginInit();
|
||||
((System.ComponentModel.ISupportInitialize)(this.pic_boardcastStatus)).BeginInit();
|
||||
this.grp_input.SuspendLayout();
|
||||
this.grp_mode.SuspendLayout();
|
||||
((System.ComponentModel.ISupportInitialize)(this.axWindowsMediaPlayer1)).BeginInit();
|
||||
this.SuspendLayout();
|
||||
//
|
||||
// grp_status
|
||||
//
|
||||
this.grp_status.Controls.Add(this.alarm1);
|
||||
this.grp_status.Controls.Add(this.lamp);
|
||||
this.grp_status.Controls.Add(this.lbl_windowopen);
|
||||
this.grp_status.Controls.Add(this.lbl_windowclose);
|
||||
this.grp_status.Controls.Add(this.trb_curtain);
|
||||
this.grp_status.Controls.Add(this.lbl_boardcast);
|
||||
this.grp_status.Controls.Add(this.lbl_curtain);
|
||||
this.grp_status.Controls.Add(this.pic_boardcastStatus);
|
||||
this.grp_status.Controls.Add(this.lbl_lights);
|
||||
this.grp_status.Location = new System.Drawing.Point(193, 97);
|
||||
this.grp_status.Margin = new System.Windows.Forms.Padding(3, 2, 3, 2);
|
||||
this.grp_status.Name = "grp_status";
|
||||
this.grp_status.Padding = new System.Windows.Forms.Padding(3, 2, 3, 2);
|
||||
this.grp_status.Size = new System.Drawing.Size(328, 286);
|
||||
this.grp_status.TabIndex = 8;
|
||||
this.grp_status.TabStop = false;
|
||||
this.grp_status.Text = "状态栏";
|
||||
//
|
||||
// alarm1
|
||||
//
|
||||
this.alarm1.BackColor = System.Drawing.Color.White;
|
||||
this.alarm1.Location = new System.Drawing.Point(37, 130);
|
||||
this.alarm1.Margin = new System.Windows.Forms.Padding(5);
|
||||
this.alarm1.Name = "alarm1";
|
||||
this.alarm1.Size = new System.Drawing.Size(7, 6);
|
||||
this.alarm1.TabIndex = 8;
|
||||
//
|
||||
// lamp
|
||||
//
|
||||
this.lamp.BackColor = System.Drawing.Color.White;
|
||||
this.lamp.Location = new System.Drawing.Point(172, 48);
|
||||
this.lamp.Margin = new System.Windows.Forms.Padding(5);
|
||||
this.lamp.Name = "lamp";
|
||||
this.lamp.Size = new System.Drawing.Size(148, 232);
|
||||
this.lamp.TabIndex = 6;
|
||||
//
|
||||
// lbl_windowopen
|
||||
//
|
||||
this.lbl_windowopen.AutoSize = true;
|
||||
this.lbl_windowopen.Location = new System.Drawing.Point(13, 101);
|
||||
this.lbl_windowopen.Name = "lbl_windowopen";
|
||||
this.lbl_windowopen.Size = new System.Drawing.Size(37, 15);
|
||||
this.lbl_windowopen.TabIndex = 5;
|
||||
this.lbl_windowopen.Text = "全开";
|
||||
//
|
||||
// lbl_windowclose
|
||||
//
|
||||
this.lbl_windowclose.AutoSize = true;
|
||||
this.lbl_windowclose.Location = new System.Drawing.Point(127, 101);
|
||||
this.lbl_windowclose.Name = "lbl_windowclose";
|
||||
this.lbl_windowclose.Size = new System.Drawing.Size(37, 15);
|
||||
this.lbl_windowclose.TabIndex = 5;
|
||||
this.lbl_windowclose.Text = "全关";
|
||||
//
|
||||
// trb_curtain
|
||||
//
|
||||
this.trb_curtain.Location = new System.Drawing.Point(16, 59);
|
||||
this.trb_curtain.Margin = new System.Windows.Forms.Padding(3, 2, 3, 2);
|
||||
this.trb_curtain.Name = "trb_curtain";
|
||||
this.trb_curtain.Size = new System.Drawing.Size(143, 56);
|
||||
this.trb_curtain.TabIndex = 4;
|
||||
this.trb_curtain.Value = 10;
|
||||
//
|
||||
// lbl_boardcast
|
||||
//
|
||||
this.lbl_boardcast.AutoSize = true;
|
||||
this.lbl_boardcast.Font = new System.Drawing.Font("宋体", 9F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(134)));
|
||||
this.lbl_boardcast.Location = new System.Drawing.Point(23, 186);
|
||||
this.lbl_boardcast.Name = "lbl_boardcast";
|
||||
this.lbl_boardcast.Size = new System.Drawing.Size(39, 15);
|
||||
this.lbl_boardcast.TabIndex = 2;
|
||||
this.lbl_boardcast.Text = "广播";
|
||||
//
|
||||
// lbl_curtain
|
||||
//
|
||||
this.lbl_curtain.AutoSize = true;
|
||||
this.lbl_curtain.Font = new System.Drawing.Font("宋体", 9F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(134)));
|
||||
this.lbl_curtain.Location = new System.Drawing.Point(13, 34);
|
||||
this.lbl_curtain.Name = "lbl_curtain";
|
||||
this.lbl_curtain.Size = new System.Drawing.Size(39, 15);
|
||||
this.lbl_curtain.TabIndex = 2;
|
||||
this.lbl_curtain.Text = "窗帘";
|
||||
//
|
||||
// pic_boardcastStatus
|
||||
//
|
||||
this.pic_boardcastStatus.Enabled = false;
|
||||
this.pic_boardcastStatus.Image = global::寝室情景模式.Properties.Resources.switch_off;
|
||||
this.pic_boardcastStatus.Location = new System.Drawing.Point(105, 182);
|
||||
this.pic_boardcastStatus.Margin = new System.Windows.Forms.Padding(3, 2, 3, 2);
|
||||
this.pic_boardcastStatus.Name = "pic_boardcastStatus";
|
||||
this.pic_boardcastStatus.Size = new System.Drawing.Size(44, 21);
|
||||
this.pic_boardcastStatus.SizeMode = System.Windows.Forms.PictureBoxSizeMode.Zoom;
|
||||
this.pic_boardcastStatus.TabIndex = 1;
|
||||
this.pic_boardcastStatus.TabStop = false;
|
||||
//
|
||||
// lbl_lights
|
||||
//
|
||||
this.lbl_lights.AutoSize = true;
|
||||
this.lbl_lights.Font = new System.Drawing.Font("宋体", 9F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(134)));
|
||||
this.lbl_lights.Location = new System.Drawing.Point(235, 25);
|
||||
this.lbl_lights.Name = "lbl_lights";
|
||||
this.lbl_lights.Size = new System.Drawing.Size(39, 15);
|
||||
this.lbl_lights.TabIndex = 0;
|
||||
this.lbl_lights.Text = "灯光";
|
||||
//
|
||||
// grp_input
|
||||
//
|
||||
this.grp_input.Controls.Add(this.txt_temperature);
|
||||
this.grp_input.Controls.Add(this.txt_humidity);
|
||||
this.grp_input.Controls.Add(this.com_import);
|
||||
this.grp_input.Controls.Add(this.lbl_temperature);
|
||||
this.grp_input.Controls.Add(this.txt_light);
|
||||
this.grp_input.Controls.Add(this.lbl_import);
|
||||
this.grp_input.Controls.Add(this.lbl_humidity);
|
||||
this.grp_input.Controls.Add(this.lbl_light);
|
||||
this.grp_input.Location = new System.Drawing.Point(10, 97);
|
||||
this.grp_input.Margin = new System.Windows.Forms.Padding(3, 2, 3, 2);
|
||||
this.grp_input.Name = "grp_input";
|
||||
this.grp_input.Padding = new System.Windows.Forms.Padding(3, 2, 3, 2);
|
||||
this.grp_input.Size = new System.Drawing.Size(177, 238);
|
||||
this.grp_input.TabIndex = 7;
|
||||
this.grp_input.TabStop = false;
|
||||
this.grp_input.Text = "采集量";
|
||||
//
|
||||
// txt_temperature
|
||||
//
|
||||
this.txt_temperature.Location = new System.Drawing.Point(75, 141);
|
||||
this.txt_temperature.Margin = new System.Windows.Forms.Padding(3, 2, 3, 2);
|
||||
this.txt_temperature.Name = "txt_temperature";
|
||||
this.txt_temperature.Size = new System.Drawing.Size(85, 25);
|
||||
this.txt_temperature.TabIndex = 3;
|
||||
//
|
||||
// txt_humidity
|
||||
//
|
||||
this.txt_humidity.Location = new System.Drawing.Point(76, 200);
|
||||
this.txt_humidity.Margin = new System.Windows.Forms.Padding(3, 2, 3, 2);
|
||||
this.txt_humidity.Name = "txt_humidity";
|
||||
this.txt_humidity.Size = new System.Drawing.Size(85, 25);
|
||||
this.txt_humidity.TabIndex = 3;
|
||||
//
|
||||
// com_import
|
||||
//
|
||||
this.com_import.FormattingEnabled = true;
|
||||
this.com_import.Location = new System.Drawing.Point(76, 25);
|
||||
this.com_import.Margin = new System.Windows.Forms.Padding(3, 2, 3, 2);
|
||||
this.com_import.Name = "com_import";
|
||||
this.com_import.Size = new System.Drawing.Size(85, 23);
|
||||
this.com_import.TabIndex = 6;
|
||||
//
|
||||
// lbl_temperature
|
||||
//
|
||||
this.lbl_temperature.AutoSize = true;
|
||||
this.lbl_temperature.Font = new System.Drawing.Font("宋体", 9F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(134)));
|
||||
this.lbl_temperature.Location = new System.Drawing.Point(29, 146);
|
||||
this.lbl_temperature.Name = "lbl_temperature";
|
||||
this.lbl_temperature.Size = new System.Drawing.Size(39, 15);
|
||||
this.lbl_temperature.TabIndex = 1;
|
||||
this.lbl_temperature.Text = "温度";
|
||||
//
|
||||
// txt_light
|
||||
//
|
||||
this.txt_light.Location = new System.Drawing.Point(76, 84);
|
||||
this.txt_light.Margin = new System.Windows.Forms.Padding(3, 2, 3, 2);
|
||||
this.txt_light.Name = "txt_light";
|
||||
this.txt_light.Size = new System.Drawing.Size(85, 25);
|
||||
this.txt_light.TabIndex = 2;
|
||||
//
|
||||
// lbl_import
|
||||
//
|
||||
this.lbl_import.AutoSize = true;
|
||||
this.lbl_import.Location = new System.Drawing.Point(31, 29);
|
||||
this.lbl_import.Name = "lbl_import";
|
||||
this.lbl_import.Size = new System.Drawing.Size(37, 15);
|
||||
this.lbl_import.TabIndex = 1;
|
||||
this.lbl_import.Text = "串口";
|
||||
//
|
||||
// lbl_humidity
|
||||
//
|
||||
this.lbl_humidity.AutoSize = true;
|
||||
this.lbl_humidity.Font = new System.Drawing.Font("宋体", 9F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(134)));
|
||||
this.lbl_humidity.Location = new System.Drawing.Point(31, 204);
|
||||
this.lbl_humidity.Name = "lbl_humidity";
|
||||
this.lbl_humidity.Size = new System.Drawing.Size(39, 15);
|
||||
this.lbl_humidity.TabIndex = 1;
|
||||
this.lbl_humidity.Text = "湿度";
|
||||
//
|
||||
// lbl_light
|
||||
//
|
||||
this.lbl_light.AutoSize = true;
|
||||
this.lbl_light.Font = new System.Drawing.Font("宋体", 9F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(134)));
|
||||
this.lbl_light.Location = new System.Drawing.Point(15, 88);
|
||||
this.lbl_light.Name = "lbl_light";
|
||||
this.lbl_light.Size = new System.Drawing.Size(55, 15);
|
||||
this.lbl_light.TabIndex = 0;
|
||||
this.lbl_light.Text = "光照度";
|
||||
//
|
||||
// grp_mode
|
||||
//
|
||||
this.grp_mode.Controls.Add(this.lbl_time);
|
||||
this.grp_mode.Controls.Add(this.txt_time);
|
||||
this.grp_mode.Controls.Add(this.lbl_mode);
|
||||
this.grp_mode.Controls.Add(this.btn_apply);
|
||||
this.grp_mode.Location = new System.Drawing.Point(11, 10);
|
||||
this.grp_mode.Margin = new System.Windows.Forms.Padding(3, 2, 3, 2);
|
||||
this.grp_mode.Name = "grp_mode";
|
||||
this.grp_mode.Padding = new System.Windows.Forms.Padding(3, 2, 3, 2);
|
||||
this.grp_mode.Size = new System.Drawing.Size(511, 83);
|
||||
this.grp_mode.TabIndex = 6;
|
||||
this.grp_mode.TabStop = false;
|
||||
this.grp_mode.Text = "模式选择";
|
||||
//
|
||||
// lbl_time
|
||||
//
|
||||
this.lbl_time.AutoSize = true;
|
||||
this.lbl_time.Font = new System.Drawing.Font("宋体", 9F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(134)));
|
||||
this.lbl_time.Location = new System.Drawing.Point(68, 20);
|
||||
this.lbl_time.Name = "lbl_time";
|
||||
this.lbl_time.Size = new System.Drawing.Size(71, 15);
|
||||
this.lbl_time.TabIndex = 9;
|
||||
this.lbl_time.Text = "当前时间";
|
||||
//
|
||||
// txt_time
|
||||
//
|
||||
this.txt_time.Location = new System.Drawing.Point(145, 16);
|
||||
this.txt_time.Margin = new System.Windows.Forms.Padding(3, 2, 3, 2);
|
||||
this.txt_time.Name = "txt_time";
|
||||
this.txt_time.Size = new System.Drawing.Size(127, 25);
|
||||
this.txt_time.TabIndex = 10;
|
||||
//
|
||||
// lbl_mode
|
||||
//
|
||||
this.lbl_mode.AutoSize = true;
|
||||
this.lbl_mode.Location = new System.Drawing.Point(82, 54);
|
||||
this.lbl_mode.Name = "lbl_mode";
|
||||
this.lbl_mode.Size = new System.Drawing.Size(0, 15);
|
||||
this.lbl_mode.TabIndex = 1;
|
||||
//
|
||||
// btn_apply
|
||||
//
|
||||
this.btn_apply.Location = new System.Drawing.Point(300, 16);
|
||||
this.btn_apply.Margin = new System.Windows.Forms.Padding(3, 2, 3, 2);
|
||||
this.btn_apply.Name = "btn_apply";
|
||||
this.btn_apply.Size = new System.Drawing.Size(129, 25);
|
||||
this.btn_apply.TabIndex = 2;
|
||||
this.btn_apply.Text = "开启情景模式";
|
||||
this.btn_apply.UseVisualStyleBackColor = true;
|
||||
this.btn_apply.Click += new System.EventHandler(this.btn_apply_Click);
|
||||
//
|
||||
// timer
|
||||
//
|
||||
this.timer.Interval = 1000;
|
||||
this.timer.Tick += new System.EventHandler(this.timer_Tick);
|
||||
//
|
||||
// axWindowsMediaPlayer1
|
||||
//
|
||||
this.axWindowsMediaPlayer1.Enabled = true;
|
||||
this.axWindowsMediaPlayer1.Location = new System.Drawing.Point(12, 345);
|
||||
this.axWindowsMediaPlayer1.Name = "axWindowsMediaPlayer1";
|
||||
this.axWindowsMediaPlayer1.OcxState = ((System.Windows.Forms.AxHost.State)(resources.GetObject("axWindowsMediaPlayer1.OcxState")));
|
||||
this.axWindowsMediaPlayer1.Size = new System.Drawing.Size(175, 38);
|
||||
this.axWindowsMediaPlayer1.TabIndex = 9;
|
||||
this.axWindowsMediaPlayer1.Visible = false;
|
||||
//
|
||||
// frm_control
|
||||
//
|
||||
this.AutoScaleDimensions = new System.Drawing.SizeF(8F, 15F);
|
||||
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
||||
this.ClientSize = new System.Drawing.Size(539, 396);
|
||||
this.Controls.Add(this.axWindowsMediaPlayer1);
|
||||
this.Controls.Add(this.grp_status);
|
||||
this.Controls.Add(this.grp_input);
|
||||
this.Controls.Add(this.grp_mode);
|
||||
this.Margin = new System.Windows.Forms.Padding(3, 2, 3, 2);
|
||||
this.Name = "frm_control";
|
||||
this.Text = "智慧寝室情景控制";
|
||||
this.Load += new System.EventHandler(this.frm_control_Load);
|
||||
this.grp_status.ResumeLayout(false);
|
||||
this.grp_status.PerformLayout();
|
||||
((System.ComponentModel.ISupportInitialize)(this.trb_curtain)).EndInit();
|
||||
((System.ComponentModel.ISupportInitialize)(this.pic_boardcastStatus)).EndInit();
|
||||
this.grp_input.ResumeLayout(false);
|
||||
this.grp_input.PerformLayout();
|
||||
this.grp_mode.ResumeLayout(false);
|
||||
this.grp_mode.PerformLayout();
|
||||
((System.ComponentModel.ISupportInitialize)(this.axWindowsMediaPlayer1)).EndInit();
|
||||
this.ResumeLayout(false);
|
||||
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private System.Windows.Forms.GroupBox grp_status;
|
||||
private WinFormsDeviceControlLibrary.Lamp lamp;
|
||||
private System.Windows.Forms.Label lbl_windowopen;
|
||||
private System.Windows.Forms.Label lbl_windowclose;
|
||||
private System.Windows.Forms.TrackBar trb_curtain;
|
||||
private System.Windows.Forms.Label lbl_boardcast;
|
||||
private System.Windows.Forms.Label lbl_curtain;
|
||||
private System.Windows.Forms.PictureBox pic_boardcastStatus;
|
||||
private System.Windows.Forms.Label lbl_lights;
|
||||
private System.Windows.Forms.GroupBox grp_input;
|
||||
private System.Windows.Forms.TextBox txt_temperature;
|
||||
private System.Windows.Forms.TextBox txt_humidity;
|
||||
private System.Windows.Forms.ComboBox com_import;
|
||||
private System.Windows.Forms.Label lbl_temperature;
|
||||
private System.Windows.Forms.TextBox txt_light;
|
||||
private System.Windows.Forms.Label lbl_import;
|
||||
private System.Windows.Forms.Label lbl_humidity;
|
||||
private System.Windows.Forms.Label lbl_light;
|
||||
private System.Windows.Forms.GroupBox grp_mode;
|
||||
private System.Windows.Forms.Label lbl_mode;
|
||||
private System.Windows.Forms.Button btn_apply;
|
||||
private System.Windows.Forms.Label lbl_time;
|
||||
private System.Windows.Forms.TextBox txt_time;
|
||||
private System.Windows.Forms.Timer timer;
|
||||
private WinFormsDeviceControlLibrary.Alarm alarm1;
|
||||
private AxWMPLib.AxWindowsMediaPlayer axWindowsMediaPlayer1;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,163 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Data;
|
||||
using System.Drawing;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Forms;
|
||||
using System.IO.Ports;//引用系统IO端口库
|
||||
using DeviceCtrlLibrary;//引用自定义设备控制库
|
||||
using DeviceCtrlLibrary.Model;//引用自定义设备控制模型库
|
||||
using System.Threading;//引用线程库
|
||||
|
||||
namespace 寝室情景模式
|
||||
{
|
||||
public partial class frm_control : Form
|
||||
{
|
||||
bool IsOpen = false;//定义布尔变量记录串口打开情况,默认为false
|
||||
bool IsLampOopen = false;//定义布尔变量记录灯光打开情况,默认为false
|
||||
bool IsBoardcastOpen = false;//定义布尔变量记录广播打开情况,默认为false
|
||||
ADAM4017ResModel ResModel;//创建4017接收数据模型
|
||||
DateTime now;
|
||||
DateTime GetUp = DateTime.Today.AddHours(6.75);//6:45起床时间
|
||||
DateTime ClassBegin = DateTime.Today.AddHours(7.5);//7:30上课开始时间
|
||||
DateTime ClassEnd = DateTime.Today.AddHours(21.0);//21:00上课结束时间
|
||||
DateTime Sleep = DateTime.Today.AddHours(22.0);//22:00睡觉时间
|
||||
public frm_control()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
private void frm_control_Load(object sender, EventArgs e)
|
||||
{
|
||||
com_import.Items.Clear();
|
||||
//将系统串口名依次加入下拉列表框中
|
||||
foreach (string item in SerialPort.GetPortNames())
|
||||
{
|
||||
com_import.Items.Add(item);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void btn_apply_Click(object sender, EventArgs e)
|
||||
{
|
||||
if (!IsOpen)
|
||||
{
|
||||
IsOpen = true;
|
||||
btn_apply.Text = "停止情景模式";
|
||||
DeviceCtrlHelper.adam4017Helper.Open(com_import.Text);
|
||||
DeviceCtrlHelper.adam4150Helper.Open(com_import.Text);
|
||||
timer.Start();
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
btn_apply.Text = "开启情景模式";
|
||||
timer.Stop();
|
||||
IsOpen = false;
|
||||
DeviceCtrlHelper.adam4017Helper.Close();
|
||||
DeviceCtrlHelper.adam4150Helper.Close();
|
||||
}
|
||||
}
|
||||
|
||||
private void CheckMode()//切换各个模式
|
||||
{
|
||||
if (now >= GetUp && now < ClassBegin)
|
||||
{
|
||||
lbl_mode.Text = "智慧寝室正处于起床模式";
|
||||
if (IsLampOopen == false)
|
||||
{
|
||||
IsLampOopen = true;
|
||||
DeviceCtrlHelper.adam4150Helper.OnOff(1, true);
|
||||
lamp.OnOff(true);
|
||||
}
|
||||
if (IsBoardcastOpen == false)
|
||||
{
|
||||
IsBoardcastOpen = true;
|
||||
Thread playthd = new Thread(PlayMp3);
|
||||
playthd.Start();
|
||||
}
|
||||
trb_curtain.Value = 5;
|
||||
|
||||
}
|
||||
else if (now >= ClassBegin && now < ClassEnd)
|
||||
{
|
||||
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 = 0;
|
||||
|
||||
}
|
||||
else if (now >= ClassEnd && now < Sleep)
|
||||
{
|
||||
lbl_mode.Text = "智慧寝室正处于回寝模式";
|
||||
if (IsLampOopen == false)
|
||||
{
|
||||
IsLampOopen = true;
|
||||
DeviceCtrlHelper.adam4150Helper.OnOff(1, true);
|
||||
lamp.OnOff(true);
|
||||
}
|
||||
if (IsBoardcastOpen == false)
|
||||
{
|
||||
IsBoardcastOpen = true;
|
||||
Thread playthd = new Thread(PlayMp3);
|
||||
playthd.Start();
|
||||
}
|
||||
|
||||
trb_curtain.Value = 10;
|
||||
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
private void timer_Tick(object sender, EventArgs e)
|
||||
{
|
||||
|
||||
now = DateTime.Now;
|
||||
txt_time.Text = now.ToString("HH:mm:ss");//显示时间
|
||||
ResModel = DeviceCtrlHelper.adam4017Helper.SetData();//采集数据
|
||||
//显示采集到的数据
|
||||
txt_light.Text = ResModel.lightValue;//ResModel.airPressureValue
|
||||
txt_temperature.Text = ResModel.temperatureValue;//ResModel.airQualityValue
|
||||
txt_humidity.Text = ResModel.humidityValue;//ResModel.CO2Value
|
||||
CheckMode();
|
||||
}
|
||||
private void PlayMp3()//播放mp3音乐
|
||||
{
|
||||
|
||||
pic_boardcastStatus.Image = Properties.Resources.switch_on;
|
||||
axWindowsMediaPlayer1.URL = System.AppDomain.CurrentDomain.BaseDirectory + "//Resources//ring.mp3";
|
||||
axWindowsMediaPlayer1.Ctlcontrols.play();
|
||||
Thread.Sleep(4000);
|
||||
pic_boardcastStatus.Image = Properties.Resources.switch_off;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace 寝室情景模式
|
||||
{
|
||||
static class Program
|
||||
{
|
||||
/// <summary>
|
||||
/// 应用程序的主入口点。
|
||||
/// </summary>
|
||||
[STAThread]
|
||||
static void Main()
|
||||
{
|
||||
Application.EnableVisualStyles();
|
||||
Application.SetCompatibleTextRenderingDefault(false);
|
||||
Application.Run(new frm_control());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// <auto-generated>
|
||||
// 项目资源(平台素材库)。在"项目资源"里添加或移除,不要手动修改这个文件。
|
||||
// </auto-generated>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace 寝室情景模式.Properties {
|
||||
|
||||
/// <summary>
|
||||
/// 项目资源:Properties.Resources.资源名
|
||||
/// </summary>
|
||||
internal class Resources {
|
||||
|
||||
/// <summary>开关:开</summary>
|
||||
internal static System.Drawing.Bitmap switch_on {
|
||||
get { return global::Sim.Host.ProjectResources.Image("switch_on.png"); }
|
||||
}
|
||||
|
||||
/// <summary>开关:关</summary>
|
||||
internal static System.Drawing.Bitmap switch_off {
|
||||
get { return global::Sim.Host.ProjectResources.Image("switch_off.png"); }
|
||||
}
|
||||
|
||||
/// <summary>铃声</summary>
|
||||
internal static byte[] ring {
|
||||
get { return global::Sim.Host.ProjectResources.Bytes("ring.mp3"); }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
# 评分规则:书上评价表的功能项。每项单独启动一次程序,按步骤操作,核对结果。
|
||||
# 情景模式按时刻切换:用 clock 拨实训台的虚拟时钟(今天的某个时刻)。格式说明见 server/src/grading/checks.ts
|
||||
- title: 能实时显示环境数据
|
||||
steps:
|
||||
- select: { control: com_import, item: COM2 }
|
||||
- click: btn_apply
|
||||
- expect: { control: btn_apply, text: 停止情景模式, desc: 按钮文字变成"停止情景模式" }
|
||||
- board: { light: 650, temperature: 23, humidity: 51 }
|
||||
- expect: { control: txt_light, text: '650', within: 5s }
|
||||
- expect: { control: txt_temperature, text: '23', within: 5s }
|
||||
- expect: { control: txt_time, matches: '^\d{2}:\d{2}:\d{2}$', desc: 显示当前时间 }
|
||||
|
||||
- title: 能根据时间段的变化判断寝室情景实时控制智能设备
|
||||
steps:
|
||||
- clock: '06:50:00'
|
||||
- select: { control: com_import, item: COM2 }
|
||||
- click: btn_apply
|
||||
- expect: { control: lbl_mode, contains: 起床模式, within: 5s, desc: 6:50 是起床模式 }
|
||||
- expect: { relay: 1, on: true, desc: 起床模式打开灯光 }
|
||||
- expect: { control: trb_curtain, value: 5, desc: 起床模式窗帘半开 }
|
||||
- expect: { media: ring.mp3, desc: 起床模式广播播放铃声 }
|
||||
- clock: '08:00:00'
|
||||
- expect: { control: lbl_mode, contains: 上课模式, within: 5s, desc: 8:00 是上课模式 }
|
||||
- expect: { relay: 1, on: false, desc: 上课模式关闭灯光 }
|
||||
- expect: { control: trb_curtain, value: 0, desc: 上课模式窗帘全开 }
|
||||
- clock: '21:30:00'
|
||||
- expect: { control: lbl_mode, contains: 回寝模式, within: 5s, desc: 21:30 是回寝模式 }
|
||||
- expect: { control: trb_curtain, value: 10, desc: 回寝模式关窗帘 }
|
||||
- clock: '23:00:00'
|
||||
- expect: { control: lbl_mode, contains: 熄灯模式, within: 5s, desc: 23:00 是熄灯模式 }
|
||||
- expect: { relay: 1, on: false, desc: 熄灯模式关闭灯光 }
|
||||
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"title": "项目7 任务2 实现智慧寝室",
|
||||
"source": "教材书 p141–145,界面和代码取自配套参考工程;开关图片和铃声来自平台素材库。按时刻切换情景模式:在实训台的时钟上拨时间"
|
||||
}
|
||||
@@ -0,0 +1,611 @@
|
||||
namespace 会议室情景模式
|
||||
{
|
||||
partial class frm_control
|
||||
{
|
||||
/// <summary>
|
||||
/// 必需的设计器变量。
|
||||
/// </summary>
|
||||
private System.ComponentModel.IContainer components = null;
|
||||
|
||||
/// <summary>
|
||||
/// 清理所有正在使用的资源。
|
||||
/// </summary>
|
||||
/// <param name="disposing">如果应释放托管资源,为 true;否则为 false。</param>
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing && (components != null))
|
||||
{
|
||||
components.Dispose();
|
||||
}
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
#region Windows 窗体设计器生成的代码
|
||||
|
||||
/// <summary>
|
||||
/// 设计器支持所需的方法 - 不要
|
||||
/// 使用代码编辑器修改此方法的内容。
|
||||
/// </summary>
|
||||
private void InitializeComponent()
|
||||
{
|
||||
this.components = new System.ComponentModel.Container();
|
||||
this.grp_status = new System.Windows.Forms.GroupBox();
|
||||
this.lbl_subMicrophone = new System.Windows.Forms.Label();
|
||||
this.lbl_mainMicrophone = new System.Windows.Forms.Label();
|
||||
this.lamp = new WinFormsDeviceControlLibrary.Lamp();
|
||||
this.lbl_curtainopen = new System.Windows.Forms.Label();
|
||||
this.lbl_curtainclose = new System.Windows.Forms.Label();
|
||||
this.trb_curtain = new System.Windows.Forms.TrackBar();
|
||||
this.lbl_microphone = new System.Windows.Forms.Label();
|
||||
this.lbl_curtain = new System.Windows.Forms.Label();
|
||||
this.pic_lightsStatus = new System.Windows.Forms.PictureBox();
|
||||
this.pic_subMicrophone = new System.Windows.Forms.PictureBox();
|
||||
this.pic_mainMicrophone = new System.Windows.Forms.PictureBox();
|
||||
this.lbl_lights = new System.Windows.Forms.Label();
|
||||
this.grp_input = new System.Windows.Forms.GroupBox();
|
||||
this.lbl_camera = new System.Windows.Forms.Label();
|
||||
this.txt_camera = new System.Windows.Forms.TextBox();
|
||||
this.txt_temperature = new System.Windows.Forms.TextBox();
|
||||
this.txt_airQuality = new System.Windows.Forms.TextBox();
|
||||
this.txt_humidity = new System.Windows.Forms.TextBox();
|
||||
this.com_import = new System.Windows.Forms.ComboBox();
|
||||
this.lbl_temperature = new System.Windows.Forms.Label();
|
||||
this.txt_airPressure = new System.Windows.Forms.TextBox();
|
||||
this.txt_light = new System.Windows.Forms.TextBox();
|
||||
this.lbl_import = new System.Windows.Forms.Label();
|
||||
this.lbl_airQuality = new System.Windows.Forms.Label();
|
||||
this.lbl_airPressure = new System.Windows.Forms.Label();
|
||||
this.lbl_humidity = new System.Windows.Forms.Label();
|
||||
this.lbl_light = new System.Windows.Forms.Label();
|
||||
this.grp_mode = new System.Windows.Forms.GroupBox();
|
||||
this.com_mode = new System.Windows.Forms.ComboBox();
|
||||
this.lbl_second = new System.Windows.Forms.Label();
|
||||
this.lbl_minute = new System.Windows.Forms.Label();
|
||||
this.lbl_hour = new System.Windows.Forms.Label();
|
||||
this.lbl_time = new System.Windows.Forms.Label();
|
||||
this.txt_second = new System.Windows.Forms.TextBox();
|
||||
this.txt_minute = new System.Windows.Forms.TextBox();
|
||||
this.txt_hour = new System.Windows.Forms.TextBox();
|
||||
this.chk_record = new System.Windows.Forms.CheckBox();
|
||||
this.lbl_mode = new System.Windows.Forms.Label();
|
||||
this.btn_exit = new System.Windows.Forms.Button();
|
||||
this.btn_reserve = new System.Windows.Forms.Button();
|
||||
this.timer = new System.Windows.Forms.Timer(this.components);
|
||||
this.grp_status.SuspendLayout();
|
||||
((System.ComponentModel.ISupportInitialize)(this.trb_curtain)).BeginInit();
|
||||
((System.ComponentModel.ISupportInitialize)(this.pic_lightsStatus)).BeginInit();
|
||||
((System.ComponentModel.ISupportInitialize)(this.pic_subMicrophone)).BeginInit();
|
||||
((System.ComponentModel.ISupportInitialize)(this.pic_mainMicrophone)).BeginInit();
|
||||
this.grp_input.SuspendLayout();
|
||||
this.grp_mode.SuspendLayout();
|
||||
this.SuspendLayout();
|
||||
//
|
||||
// grp_status
|
||||
//
|
||||
this.grp_status.Controls.Add(this.lbl_subMicrophone);
|
||||
this.grp_status.Controls.Add(this.lbl_mainMicrophone);
|
||||
this.grp_status.Controls.Add(this.lamp);
|
||||
this.grp_status.Controls.Add(this.lbl_curtainopen);
|
||||
this.grp_status.Controls.Add(this.lbl_curtainclose);
|
||||
this.grp_status.Controls.Add(this.trb_curtain);
|
||||
this.grp_status.Controls.Add(this.lbl_microphone);
|
||||
this.grp_status.Controls.Add(this.lbl_curtain);
|
||||
this.grp_status.Controls.Add(this.pic_lightsStatus);
|
||||
this.grp_status.Controls.Add(this.pic_subMicrophone);
|
||||
this.grp_status.Controls.Add(this.pic_mainMicrophone);
|
||||
this.grp_status.Controls.Add(this.lbl_lights);
|
||||
this.grp_status.Location = new System.Drawing.Point(145, 100);
|
||||
this.grp_status.Margin = new System.Windows.Forms.Padding(2, 2, 2, 2);
|
||||
this.grp_status.Name = "grp_status";
|
||||
this.grp_status.Padding = new System.Windows.Forms.Padding(2, 2, 2, 2);
|
||||
this.grp_status.Size = new System.Drawing.Size(294, 270);
|
||||
this.grp_status.TabIndex = 11;
|
||||
this.grp_status.TabStop = false;
|
||||
this.grp_status.Text = "状态栏";
|
||||
//
|
||||
// lbl_subMicrophone
|
||||
//
|
||||
this.lbl_subMicrophone.AutoSize = true;
|
||||
this.lbl_subMicrophone.Location = new System.Drawing.Point(113, 140);
|
||||
this.lbl_subMicrophone.Margin = new System.Windows.Forms.Padding(2, 0, 2, 0);
|
||||
this.lbl_subMicrophone.Name = "lbl_subMicrophone";
|
||||
this.lbl_subMicrophone.Size = new System.Drawing.Size(17, 12);
|
||||
this.lbl_subMicrophone.TabIndex = 7;
|
||||
this.lbl_subMicrophone.Text = "客";
|
||||
//
|
||||
// lbl_mainMicrophone
|
||||
//
|
||||
this.lbl_mainMicrophone.AutoSize = true;
|
||||
this.lbl_mainMicrophone.Location = new System.Drawing.Point(64, 140);
|
||||
this.lbl_mainMicrophone.Margin = new System.Windows.Forms.Padding(2, 0, 2, 0);
|
||||
this.lbl_mainMicrophone.Name = "lbl_mainMicrophone";
|
||||
this.lbl_mainMicrophone.Size = new System.Drawing.Size(17, 12);
|
||||
this.lbl_mainMicrophone.TabIndex = 7;
|
||||
this.lbl_mainMicrophone.Text = "主";
|
||||
//
|
||||
// lamp
|
||||
//
|
||||
this.lamp.BackColor = System.Drawing.Color.White;
|
||||
this.lamp.Location = new System.Drawing.Point(175, 65);
|
||||
this.lamp.Name = "lamp";
|
||||
this.lamp.Size = new System.Drawing.Size(104, 177);
|
||||
this.lamp.TabIndex = 6;
|
||||
//
|
||||
// lbl_curtainopen
|
||||
//
|
||||
this.lbl_curtainopen.AutoSize = true;
|
||||
this.lbl_curtainopen.Location = new System.Drawing.Point(10, 74);
|
||||
this.lbl_curtainopen.Margin = new System.Windows.Forms.Padding(2, 0, 2, 0);
|
||||
this.lbl_curtainopen.Name = "lbl_curtainopen";
|
||||
this.lbl_curtainopen.Size = new System.Drawing.Size(29, 12);
|
||||
this.lbl_curtainopen.TabIndex = 5;
|
||||
this.lbl_curtainopen.Text = "全开";
|
||||
//
|
||||
// lbl_curtainclose
|
||||
//
|
||||
this.lbl_curtainclose.AutoSize = true;
|
||||
this.lbl_curtainclose.Location = new System.Drawing.Point(95, 74);
|
||||
this.lbl_curtainclose.Margin = new System.Windows.Forms.Padding(2, 0, 2, 0);
|
||||
this.lbl_curtainclose.Name = "lbl_curtainclose";
|
||||
this.lbl_curtainclose.Size = new System.Drawing.Size(29, 12);
|
||||
this.lbl_curtainclose.TabIndex = 5;
|
||||
this.lbl_curtainclose.Text = "全关";
|
||||
//
|
||||
// trb_curtain
|
||||
//
|
||||
this.trb_curtain.Location = new System.Drawing.Point(12, 47);
|
||||
this.trb_curtain.Margin = new System.Windows.Forms.Padding(2, 2, 2, 2);
|
||||
this.trb_curtain.Name = "trb_curtain";
|
||||
this.trb_curtain.Size = new System.Drawing.Size(107, 45);
|
||||
this.trb_curtain.TabIndex = 4;
|
||||
this.trb_curtain.Value = 10;
|
||||
//
|
||||
// lbl_microphone
|
||||
//
|
||||
this.lbl_microphone.AutoSize = true;
|
||||
this.lbl_microphone.Font = new System.Drawing.Font("宋体", 9F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(134)));
|
||||
this.lbl_microphone.Location = new System.Drawing.Point(10, 121);
|
||||
this.lbl_microphone.Margin = new System.Windows.Forms.Padding(2, 0, 2, 0);
|
||||
this.lbl_microphone.Name = "lbl_microphone";
|
||||
this.lbl_microphone.Size = new System.Drawing.Size(31, 12);
|
||||
this.lbl_microphone.TabIndex = 2;
|
||||
this.lbl_microphone.Text = "话筒";
|
||||
//
|
||||
// lbl_curtain
|
||||
//
|
||||
this.lbl_curtain.AutoSize = true;
|
||||
this.lbl_curtain.Font = new System.Drawing.Font("宋体", 9F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(134)));
|
||||
this.lbl_curtain.Location = new System.Drawing.Point(10, 35);
|
||||
this.lbl_curtain.Margin = new System.Windows.Forms.Padding(2, 0, 2, 0);
|
||||
this.lbl_curtain.Name = "lbl_curtain";
|
||||
this.lbl_curtain.Size = new System.Drawing.Size(31, 12);
|
||||
this.lbl_curtain.TabIndex = 2;
|
||||
this.lbl_curtain.Text = "窗帘";
|
||||
//
|
||||
// pic_lightsStatus
|
||||
//
|
||||
this.pic_lightsStatus.Image = global::会议室情景模式.Properties.Resources.switch_off;
|
||||
this.pic_lightsStatus.Location = new System.Drawing.Point(211, 35);
|
||||
this.pic_lightsStatus.Margin = new System.Windows.Forms.Padding(2, 2, 2, 2);
|
||||
this.pic_lightsStatus.Name = "pic_lightsStatus";
|
||||
this.pic_lightsStatus.Size = new System.Drawing.Size(33, 17);
|
||||
this.pic_lightsStatus.SizeMode = System.Windows.Forms.PictureBoxSizeMode.Zoom;
|
||||
this.pic_lightsStatus.TabIndex = 1;
|
||||
this.pic_lightsStatus.TabStop = false;
|
||||
this.pic_lightsStatus.Click += new System.EventHandler(this.pic_lightsStatus_Click);
|
||||
//
|
||||
// pic_subMicrophone
|
||||
//
|
||||
this.pic_subMicrophone.Image = global::会议室情景模式.Properties.Resources.switch_off;
|
||||
this.pic_subMicrophone.Location = new System.Drawing.Point(105, 118);
|
||||
this.pic_subMicrophone.Margin = new System.Windows.Forms.Padding(2, 2, 2, 2);
|
||||
this.pic_subMicrophone.Name = "pic_subMicrophone";
|
||||
this.pic_subMicrophone.Size = new System.Drawing.Size(33, 17);
|
||||
this.pic_subMicrophone.SizeMode = System.Windows.Forms.PictureBoxSizeMode.Zoom;
|
||||
this.pic_subMicrophone.TabIndex = 1;
|
||||
this.pic_subMicrophone.TabStop = false;
|
||||
this.pic_subMicrophone.Click += new System.EventHandler(this.pic_subMicrophone_Click);
|
||||
//
|
||||
// pic_mainMicrophone
|
||||
//
|
||||
this.pic_mainMicrophone.Image = global::会议室情景模式.Properties.Resources.switch_off;
|
||||
this.pic_mainMicrophone.Location = new System.Drawing.Point(57, 118);
|
||||
this.pic_mainMicrophone.Margin = new System.Windows.Forms.Padding(2, 2, 2, 2);
|
||||
this.pic_mainMicrophone.Name = "pic_mainMicrophone";
|
||||
this.pic_mainMicrophone.Size = new System.Drawing.Size(33, 17);
|
||||
this.pic_mainMicrophone.SizeMode = System.Windows.Forms.PictureBoxSizeMode.Zoom;
|
||||
this.pic_mainMicrophone.TabIndex = 1;
|
||||
this.pic_mainMicrophone.TabStop = false;
|
||||
this.pic_mainMicrophone.Click += new System.EventHandler(this.pic_mainMicrophone_Click);
|
||||
//
|
||||
// lbl_lights
|
||||
//
|
||||
this.lbl_lights.AutoSize = true;
|
||||
this.lbl_lights.Font = new System.Drawing.Font("宋体", 9F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(134)));
|
||||
this.lbl_lights.Location = new System.Drawing.Point(213, 21);
|
||||
this.lbl_lights.Margin = new System.Windows.Forms.Padding(2, 0, 2, 0);
|
||||
this.lbl_lights.Name = "lbl_lights";
|
||||
this.lbl_lights.Size = new System.Drawing.Size(31, 12);
|
||||
this.lbl_lights.TabIndex = 0;
|
||||
this.lbl_lights.Text = "灯光";
|
||||
//
|
||||
// grp_input
|
||||
//
|
||||
this.grp_input.Controls.Add(this.lbl_camera);
|
||||
this.grp_input.Controls.Add(this.txt_camera);
|
||||
this.grp_input.Controls.Add(this.txt_temperature);
|
||||
this.grp_input.Controls.Add(this.txt_airQuality);
|
||||
this.grp_input.Controls.Add(this.txt_humidity);
|
||||
this.grp_input.Controls.Add(this.com_import);
|
||||
this.grp_input.Controls.Add(this.lbl_temperature);
|
||||
this.grp_input.Controls.Add(this.txt_airPressure);
|
||||
this.grp_input.Controls.Add(this.txt_light);
|
||||
this.grp_input.Controls.Add(this.lbl_import);
|
||||
this.grp_input.Controls.Add(this.lbl_airQuality);
|
||||
this.grp_input.Controls.Add(this.lbl_airPressure);
|
||||
this.grp_input.Controls.Add(this.lbl_humidity);
|
||||
this.grp_input.Controls.Add(this.lbl_light);
|
||||
this.grp_input.Location = new System.Drawing.Point(8, 100);
|
||||
this.grp_input.Margin = new System.Windows.Forms.Padding(2, 2, 2, 2);
|
||||
this.grp_input.Name = "grp_input";
|
||||
this.grp_input.Padding = new System.Windows.Forms.Padding(2, 2, 2, 2);
|
||||
this.grp_input.Size = new System.Drawing.Size(133, 270);
|
||||
this.grp_input.TabIndex = 10;
|
||||
this.grp_input.TabStop = false;
|
||||
this.grp_input.Text = "采集量";
|
||||
//
|
||||
// lbl_camera
|
||||
//
|
||||
this.lbl_camera.AutoSize = true;
|
||||
this.lbl_camera.Location = new System.Drawing.Point(26, 222);
|
||||
this.lbl_camera.Margin = new System.Windows.Forms.Padding(2, 0, 2, 0);
|
||||
this.lbl_camera.Name = "lbl_camera";
|
||||
this.lbl_camera.Size = new System.Drawing.Size(77, 12);
|
||||
this.lbl_camera.TabIndex = 8;
|
||||
this.lbl_camera.Text = "摄像头IP地址";
|
||||
//
|
||||
// txt_camera
|
||||
//
|
||||
this.txt_camera.Location = new System.Drawing.Point(19, 244);
|
||||
this.txt_camera.Margin = new System.Windows.Forms.Padding(2, 2, 2, 2);
|
||||
this.txt_camera.Name = "txt_camera";
|
||||
this.txt_camera.Size = new System.Drawing.Size(95, 21);
|
||||
this.txt_camera.TabIndex = 9;
|
||||
this.txt_camera.Text = "192.168.14.131";
|
||||
//
|
||||
// txt_temperature
|
||||
//
|
||||
this.txt_temperature.Location = new System.Drawing.Point(62, 80);
|
||||
this.txt_temperature.Margin = new System.Windows.Forms.Padding(2, 2, 2, 2);
|
||||
this.txt_temperature.Name = "txt_temperature";
|
||||
this.txt_temperature.Size = new System.Drawing.Size(65, 21);
|
||||
this.txt_temperature.TabIndex = 3;
|
||||
//
|
||||
// txt_airQuality
|
||||
//
|
||||
this.txt_airQuality.Location = new System.Drawing.Point(62, 190);
|
||||
this.txt_airQuality.Margin = new System.Windows.Forms.Padding(2, 2, 2, 2);
|
||||
this.txt_airQuality.Name = "txt_airQuality";
|
||||
this.txt_airQuality.Size = new System.Drawing.Size(65, 21);
|
||||
this.txt_airQuality.TabIndex = 3;
|
||||
//
|
||||
// txt_humidity
|
||||
//
|
||||
this.txt_humidity.Location = new System.Drawing.Point(62, 117);
|
||||
this.txt_humidity.Margin = new System.Windows.Forms.Padding(2, 2, 2, 2);
|
||||
this.txt_humidity.Name = "txt_humidity";
|
||||
this.txt_humidity.Size = new System.Drawing.Size(65, 21);
|
||||
this.txt_humidity.TabIndex = 3;
|
||||
//
|
||||
// com_import
|
||||
//
|
||||
this.com_import.FormattingEnabled = true;
|
||||
this.com_import.Location = new System.Drawing.Point(57, 13);
|
||||
this.com_import.Margin = new System.Windows.Forms.Padding(2, 2, 2, 2);
|
||||
this.com_import.Name = "com_import";
|
||||
this.com_import.Size = new System.Drawing.Size(65, 20);
|
||||
this.com_import.TabIndex = 6;
|
||||
//
|
||||
// lbl_temperature
|
||||
//
|
||||
this.lbl_temperature.AutoSize = true;
|
||||
this.lbl_temperature.Font = new System.Drawing.Font("宋体", 9F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(134)));
|
||||
this.lbl_temperature.Location = new System.Drawing.Point(30, 83);
|
||||
this.lbl_temperature.Margin = new System.Windows.Forms.Padding(2, 0, 2, 0);
|
||||
this.lbl_temperature.Name = "lbl_temperature";
|
||||
this.lbl_temperature.Size = new System.Drawing.Size(31, 12);
|
||||
this.lbl_temperature.TabIndex = 1;
|
||||
this.lbl_temperature.Text = "温度";
|
||||
//
|
||||
// txt_airPressure
|
||||
//
|
||||
this.txt_airPressure.Location = new System.Drawing.Point(62, 153);
|
||||
this.txt_airPressure.Margin = new System.Windows.Forms.Padding(2, 2, 2, 2);
|
||||
this.txt_airPressure.Name = "txt_airPressure";
|
||||
this.txt_airPressure.Size = new System.Drawing.Size(65, 21);
|
||||
this.txt_airPressure.TabIndex = 2;
|
||||
//
|
||||
// txt_light
|
||||
//
|
||||
this.txt_light.Location = new System.Drawing.Point(62, 43);
|
||||
this.txt_light.Margin = new System.Windows.Forms.Padding(2, 2, 2, 2);
|
||||
this.txt_light.Name = "txt_light";
|
||||
this.txt_light.Size = new System.Drawing.Size(65, 21);
|
||||
this.txt_light.TabIndex = 2;
|
||||
//
|
||||
// lbl_import
|
||||
//
|
||||
this.lbl_import.AutoSize = true;
|
||||
this.lbl_import.Location = new System.Drawing.Point(23, 17);
|
||||
this.lbl_import.Margin = new System.Windows.Forms.Padding(2, 0, 2, 0);
|
||||
this.lbl_import.Name = "lbl_import";
|
||||
this.lbl_import.Size = new System.Drawing.Size(29, 12);
|
||||
this.lbl_import.TabIndex = 1;
|
||||
this.lbl_import.Text = "串口";
|
||||
//
|
||||
// lbl_airQuality
|
||||
//
|
||||
this.lbl_airQuality.AutoSize = true;
|
||||
this.lbl_airQuality.Font = new System.Drawing.Font("宋体", 9F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(134)));
|
||||
this.lbl_airQuality.Location = new System.Drawing.Point(5, 193);
|
||||
this.lbl_airQuality.Margin = new System.Windows.Forms.Padding(2, 0, 2, 0);
|
||||
this.lbl_airQuality.Name = "lbl_airQuality";
|
||||
this.lbl_airQuality.Size = new System.Drawing.Size(57, 12);
|
||||
this.lbl_airQuality.TabIndex = 1;
|
||||
this.lbl_airQuality.Text = "空气质量";
|
||||
//
|
||||
// lbl_airPressure
|
||||
//
|
||||
this.lbl_airPressure.AutoSize = true;
|
||||
this.lbl_airPressure.Font = new System.Drawing.Font("宋体", 9F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(134)));
|
||||
this.lbl_airPressure.Location = new System.Drawing.Point(30, 157);
|
||||
this.lbl_airPressure.Margin = new System.Windows.Forms.Padding(2, 0, 2, 0);
|
||||
this.lbl_airPressure.Name = "lbl_airPressure";
|
||||
this.lbl_airPressure.Size = new System.Drawing.Size(31, 12);
|
||||
this.lbl_airPressure.TabIndex = 0;
|
||||
this.lbl_airPressure.Text = "气压";
|
||||
//
|
||||
// lbl_humidity
|
||||
//
|
||||
this.lbl_humidity.AutoSize = true;
|
||||
this.lbl_humidity.Font = new System.Drawing.Font("宋体", 9F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(134)));
|
||||
this.lbl_humidity.Location = new System.Drawing.Point(30, 120);
|
||||
this.lbl_humidity.Margin = new System.Windows.Forms.Padding(2, 0, 2, 0);
|
||||
this.lbl_humidity.Name = "lbl_humidity";
|
||||
this.lbl_humidity.Size = new System.Drawing.Size(31, 12);
|
||||
this.lbl_humidity.TabIndex = 1;
|
||||
this.lbl_humidity.Text = "湿度";
|
||||
//
|
||||
// lbl_light
|
||||
//
|
||||
this.lbl_light.AutoSize = true;
|
||||
this.lbl_light.Font = new System.Drawing.Font("宋体", 9F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(134)));
|
||||
this.lbl_light.Location = new System.Drawing.Point(17, 47);
|
||||
this.lbl_light.Margin = new System.Windows.Forms.Padding(2, 0, 2, 0);
|
||||
this.lbl_light.Name = "lbl_light";
|
||||
this.lbl_light.Size = new System.Drawing.Size(44, 12);
|
||||
this.lbl_light.TabIndex = 0;
|
||||
this.lbl_light.Text = "光照度";
|
||||
//
|
||||
// grp_mode
|
||||
//
|
||||
this.grp_mode.Controls.Add(this.com_mode);
|
||||
this.grp_mode.Controls.Add(this.lbl_second);
|
||||
this.grp_mode.Controls.Add(this.lbl_minute);
|
||||
this.grp_mode.Controls.Add(this.lbl_hour);
|
||||
this.grp_mode.Controls.Add(this.lbl_time);
|
||||
this.grp_mode.Controls.Add(this.txt_second);
|
||||
this.grp_mode.Controls.Add(this.txt_minute);
|
||||
this.grp_mode.Controls.Add(this.txt_hour);
|
||||
this.grp_mode.Controls.Add(this.chk_record);
|
||||
this.grp_mode.Controls.Add(this.lbl_mode);
|
||||
this.grp_mode.Controls.Add(this.btn_exit);
|
||||
this.grp_mode.Controls.Add(this.btn_reserve);
|
||||
this.grp_mode.Location = new System.Drawing.Point(8, 8);
|
||||
this.grp_mode.Margin = new System.Windows.Forms.Padding(2, 2, 2, 2);
|
||||
this.grp_mode.Name = "grp_mode";
|
||||
this.grp_mode.Padding = new System.Windows.Forms.Padding(2, 2, 2, 2);
|
||||
this.grp_mode.Size = new System.Drawing.Size(431, 88);
|
||||
this.grp_mode.TabIndex = 9;
|
||||
this.grp_mode.TabStop = false;
|
||||
this.grp_mode.Text = "模式选择";
|
||||
//
|
||||
// com_mode
|
||||
//
|
||||
this.com_mode.FormattingEnabled = true;
|
||||
this.com_mode.Location = new System.Drawing.Point(191, 53);
|
||||
this.com_mode.Margin = new System.Windows.Forms.Padding(2, 2, 2, 2);
|
||||
this.com_mode.Name = "com_mode";
|
||||
this.com_mode.Size = new System.Drawing.Size(61, 20);
|
||||
this.com_mode.TabIndex = 12;
|
||||
this.com_mode.Text = "会议";
|
||||
//
|
||||
// lbl_second
|
||||
//
|
||||
this.lbl_second.AutoSize = true;
|
||||
this.lbl_second.Location = new System.Drawing.Point(127, 57);
|
||||
this.lbl_second.Margin = new System.Windows.Forms.Padding(2, 0, 2, 0);
|
||||
this.lbl_second.Name = "lbl_second";
|
||||
this.lbl_second.Size = new System.Drawing.Size(65, 12);
|
||||
this.lbl_second.TabIndex = 11;
|
||||
this.lbl_second.Text = "秒将会有场";
|
||||
//
|
||||
// lbl_minute
|
||||
//
|
||||
this.lbl_minute.AutoSize = true;
|
||||
this.lbl_minute.Location = new System.Drawing.Point(91, 57);
|
||||
this.lbl_minute.Margin = new System.Windows.Forms.Padding(2, 0, 2, 0);
|
||||
this.lbl_minute.Name = "lbl_minute";
|
||||
this.lbl_minute.Size = new System.Drawing.Size(17, 12);
|
||||
this.lbl_minute.TabIndex = 11;
|
||||
this.lbl_minute.Text = "分";
|
||||
//
|
||||
// lbl_hour
|
||||
//
|
||||
this.lbl_hour.AutoSize = true;
|
||||
this.lbl_hour.Location = new System.Drawing.Point(56, 57);
|
||||
this.lbl_hour.Margin = new System.Windows.Forms.Padding(2, 0, 2, 0);
|
||||
this.lbl_hour.Name = "lbl_hour";
|
||||
this.lbl_hour.Size = new System.Drawing.Size(17, 12);
|
||||
this.lbl_hour.TabIndex = 11;
|
||||
this.lbl_hour.Text = "时";
|
||||
//
|
||||
// lbl_time
|
||||
//
|
||||
this.lbl_time.AutoSize = true;
|
||||
this.lbl_time.Font = new System.Drawing.Font("宋体", 9F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(134)));
|
||||
this.lbl_time.Location = new System.Drawing.Point(17, 27);
|
||||
this.lbl_time.Margin = new System.Windows.Forms.Padding(2, 0, 2, 0);
|
||||
this.lbl_time.Name = "lbl_time";
|
||||
this.lbl_time.Size = new System.Drawing.Size(126, 12);
|
||||
this.lbl_time.TabIndex = 9;
|
||||
this.lbl_time.Text = "当前时间:00:00:00";
|
||||
//
|
||||
// txt_second
|
||||
//
|
||||
this.txt_second.Location = new System.Drawing.Point(109, 53);
|
||||
this.txt_second.Margin = new System.Windows.Forms.Padding(2, 2, 2, 2);
|
||||
this.txt_second.Name = "txt_second";
|
||||
this.txt_second.Size = new System.Drawing.Size(19, 21);
|
||||
this.txt_second.TabIndex = 10;
|
||||
this.txt_second.Text = "00";
|
||||
//
|
||||
// txt_minute
|
||||
//
|
||||
this.txt_minute.Location = new System.Drawing.Point(73, 53);
|
||||
this.txt_minute.Margin = new System.Windows.Forms.Padding(2, 2, 2, 2);
|
||||
this.txt_minute.Name = "txt_minute";
|
||||
this.txt_minute.Size = new System.Drawing.Size(19, 21);
|
||||
this.txt_minute.TabIndex = 10;
|
||||
this.txt_minute.Text = "20";
|
||||
//
|
||||
// txt_hour
|
||||
//
|
||||
this.txt_hour.Location = new System.Drawing.Point(38, 53);
|
||||
this.txt_hour.Margin = new System.Windows.Forms.Padding(2, 2, 2, 2);
|
||||
this.txt_hour.Name = "txt_hour";
|
||||
this.txt_hour.Size = new System.Drawing.Size(19, 21);
|
||||
this.txt_hour.TabIndex = 10;
|
||||
this.txt_hour.Text = "13";
|
||||
//
|
||||
// chk_record
|
||||
//
|
||||
this.chk_record.AutoSize = true;
|
||||
this.chk_record.Location = new System.Drawing.Point(344, 26);
|
||||
this.chk_record.Margin = new System.Windows.Forms.Padding(2, 2, 2, 2);
|
||||
this.chk_record.Name = "chk_record";
|
||||
this.chk_record.Size = new System.Drawing.Size(72, 16);
|
||||
this.chk_record.TabIndex = 3;
|
||||
this.chk_record.Text = "拍照记录";
|
||||
this.chk_record.UseVisualStyleBackColor = true;
|
||||
//
|
||||
// lbl_mode
|
||||
//
|
||||
this.lbl_mode.AutoSize = true;
|
||||
this.lbl_mode.Location = new System.Drawing.Point(162, 27);
|
||||
this.lbl_mode.Margin = new System.Windows.Forms.Padding(2, 0, 2, 0);
|
||||
this.lbl_mode.Name = "lbl_mode";
|
||||
this.lbl_mode.Size = new System.Drawing.Size(149, 12);
|
||||
this.lbl_mode.TabIndex = 1;
|
||||
this.lbl_mode.Text = "智慧会议室未开启情景模式";
|
||||
//
|
||||
// btn_exit
|
||||
//
|
||||
this.btn_exit.Location = new System.Drawing.Point(345, 49);
|
||||
this.btn_exit.Margin = new System.Windows.Forms.Padding(2, 2, 2, 2);
|
||||
this.btn_exit.Name = "btn_exit";
|
||||
this.btn_exit.Size = new System.Drawing.Size(51, 27);
|
||||
this.btn_exit.TabIndex = 2;
|
||||
this.btn_exit.Text = "退出";
|
||||
this.btn_exit.UseVisualStyleBackColor = true;
|
||||
this.btn_exit.Click += new System.EventHandler(this.btn_exit_Click);
|
||||
//
|
||||
// btn_reserve
|
||||
//
|
||||
this.btn_reserve.Location = new System.Drawing.Point(277, 49);
|
||||
this.btn_reserve.Margin = new System.Windows.Forms.Padding(2, 2, 2, 2);
|
||||
this.btn_reserve.Name = "btn_reserve";
|
||||
this.btn_reserve.Size = new System.Drawing.Size(51, 27);
|
||||
this.btn_reserve.TabIndex = 2;
|
||||
this.btn_reserve.Text = "预约";
|
||||
this.btn_reserve.UseVisualStyleBackColor = true;
|
||||
this.btn_reserve.Click += new System.EventHandler(this.btn_reserve_Click);
|
||||
//
|
||||
// timer
|
||||
//
|
||||
this.timer.Interval = 1000;
|
||||
this.timer.Tick += new System.EventHandler(this.timer_Tick);
|
||||
//
|
||||
// frm_control
|
||||
//
|
||||
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 12F);
|
||||
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
||||
this.ClientSize = new System.Drawing.Size(447, 376);
|
||||
this.Controls.Add(this.grp_status);
|
||||
this.Controls.Add(this.grp_input);
|
||||
this.Controls.Add(this.grp_mode);
|
||||
this.Margin = new System.Windows.Forms.Padding(2, 2, 2, 2);
|
||||
this.Name = "frm_control";
|
||||
this.Text = "智慧会议室情景模式";
|
||||
this.Load += new System.EventHandler(this.frm_control_Load);
|
||||
this.grp_status.ResumeLayout(false);
|
||||
this.grp_status.PerformLayout();
|
||||
((System.ComponentModel.ISupportInitialize)(this.trb_curtain)).EndInit();
|
||||
((System.ComponentModel.ISupportInitialize)(this.pic_lightsStatus)).EndInit();
|
||||
((System.ComponentModel.ISupportInitialize)(this.pic_subMicrophone)).EndInit();
|
||||
((System.ComponentModel.ISupportInitialize)(this.pic_mainMicrophone)).EndInit();
|
||||
this.grp_input.ResumeLayout(false);
|
||||
this.grp_input.PerformLayout();
|
||||
this.grp_mode.ResumeLayout(false);
|
||||
this.grp_mode.PerformLayout();
|
||||
this.ResumeLayout(false);
|
||||
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private System.Windows.Forms.GroupBox grp_status;
|
||||
private WinFormsDeviceControlLibrary.Lamp lamp;
|
||||
private System.Windows.Forms.Label lbl_curtainopen;
|
||||
private System.Windows.Forms.Label lbl_curtainclose;
|
||||
private System.Windows.Forms.TrackBar trb_curtain;
|
||||
private System.Windows.Forms.Label lbl_microphone;
|
||||
private System.Windows.Forms.Label lbl_curtain;
|
||||
private System.Windows.Forms.PictureBox pic_lightsStatus;
|
||||
private System.Windows.Forms.PictureBox pic_mainMicrophone;
|
||||
private System.Windows.Forms.Label lbl_lights;
|
||||
private System.Windows.Forms.GroupBox grp_input;
|
||||
private System.Windows.Forms.TextBox txt_temperature;
|
||||
private System.Windows.Forms.TextBox txt_humidity;
|
||||
private System.Windows.Forms.ComboBox com_import;
|
||||
private System.Windows.Forms.Label lbl_temperature;
|
||||
private System.Windows.Forms.TextBox txt_light;
|
||||
private System.Windows.Forms.Label lbl_import;
|
||||
private System.Windows.Forms.Label lbl_humidity;
|
||||
private System.Windows.Forms.Label lbl_light;
|
||||
private System.Windows.Forms.GroupBox grp_mode;
|
||||
private System.Windows.Forms.Label lbl_time;
|
||||
private System.Windows.Forms.TextBox txt_hour;
|
||||
private System.Windows.Forms.CheckBox chk_record;
|
||||
private System.Windows.Forms.Label lbl_mode;
|
||||
private System.Windows.Forms.Button btn_reserve;
|
||||
private System.Windows.Forms.TextBox txt_airQuality;
|
||||
private System.Windows.Forms.TextBox txt_airPressure;
|
||||
private System.Windows.Forms.Label lbl_airQuality;
|
||||
private System.Windows.Forms.Label lbl_airPressure;
|
||||
private System.Windows.Forms.ComboBox com_mode;
|
||||
private System.Windows.Forms.Label lbl_second;
|
||||
private System.Windows.Forms.Label lbl_minute;
|
||||
private System.Windows.Forms.Label lbl_hour;
|
||||
private System.Windows.Forms.TextBox txt_second;
|
||||
private System.Windows.Forms.TextBox txt_minute;
|
||||
private System.Windows.Forms.Timer timer;
|
||||
private System.Windows.Forms.Button btn_exit;
|
||||
private System.Windows.Forms.Label lbl_subMicrophone;
|
||||
private System.Windows.Forms.Label lbl_mainMicrophone;
|
||||
private System.Windows.Forms.PictureBox pic_subMicrophone;
|
||||
private System.Windows.Forms.Label lbl_camera;
|
||||
private System.Windows.Forms.TextBox txt_camera;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,200 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Data;
|
||||
using System.Drawing;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Forms;
|
||||
using System.IO;//引用系统IO库
|
||||
using System.IO.Ports;//引用系统IO端口库
|
||||
using DeviceCtrlLibrary;//引用自定义设备控制库
|
||||
using DeviceCtrlLibrary.Model;//引用自定义设备控制模型库
|
||||
|
||||
namespace 会议室情景模式
|
||||
{
|
||||
public partial class frm_control : Form
|
||||
{
|
||||
bool IsLightsOpen = false;//定义布尔变量记录电灯打开情况,默认为false
|
||||
bool IsmainMicrophoneOpen = false;//定义布尔变量记录主麦克风打开情况,默认为false
|
||||
bool IssubMicrophoneOpen = false;//定义布尔变量记录客麦克风打开情况,默认为false
|
||||
bool IsInitialized = false;//定义布尔变量记录情景模式是否初始化,默认为false
|
||||
ADAM4017ResModel ResModel;//创建4017接收数据模型
|
||||
DateTime now;//定义时间变量记录现在的时间
|
||||
DateTime reserve=DateTime.Now.AddDays(1) ;//定义时间变量记录预约的时间,默认为明天,防止初始值小于当前时间的假预约情况
|
||||
Image ReadImage = null;//定义图片变量记录拍摄的图片,默认为空
|
||||
|
||||
public frm_control()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
private void frm_control_Load(object sender, EventArgs e)
|
||||
{
|
||||
com_import.Items.Clear();
|
||||
com_mode.Items.Clear();
|
||||
//将系统串口名依次加入下拉列表框中
|
||||
foreach (string item in SerialPort.GetPortNames())
|
||||
{
|
||||
com_import.Items.Add(item);
|
||||
}
|
||||
com_mode.Items.Add("会议");
|
||||
com_mode.Items.Add("讨论");
|
||||
com_mode.Items.Add("展示");
|
||||
timer.Start();
|
||||
//判断拍照保存文件夹是否存在,若无则先创建
|
||||
if (!Directory.Exists(AppDomain.CurrentDomain.BaseDirectory + "Camera/"))
|
||||
Directory.CreateDirectory(AppDomain.CurrentDomain.BaseDirectory + "Camera/");
|
||||
|
||||
DeviceCtrlHelper.ipCameraHelper.ImageRead += ipCameraHelper_ImageRead;
|
||||
}
|
||||
|
||||
private void btn_reserve_Click(object sender, EventArgs e)
|
||||
{
|
||||
reserve = DateTime.Today.AddHours(Convert.ToInt16(txt_hour.Text));//设定预约时间
|
||||
reserve = reserve.AddMinutes(Convert.ToInt16(txt_minute.Text));
|
||||
reserve = reserve.AddSeconds(Convert.ToInt16(txt_second.Text));
|
||||
}
|
||||
|
||||
void ipCameraHelper_ImageRead(ImageReadEvenArgs e)
|
||||
{
|
||||
ReadImage = e.img;
|
||||
}
|
||||
|
||||
private void ChooseMode()
|
||||
{
|
||||
lbl_mode.Text = "智慧会议室已开启" + com_mode.Text + "模式";
|
||||
switch (com_mode.Text)
|
||||
{
|
||||
case "会议":
|
||||
pic_lightsStatus.Image = Properties.Resources.switch_on;//转换灯光图片
|
||||
lamp.OnOff(true);//开启界面灯光
|
||||
DeviceCtrlHelper.adam4150Helper.OnOff(1,true);//打开1通道灯
|
||||
trb_curtain.Value = 0;//打开窗帘
|
||||
pic_mainMicrophone.Image = Properties.Resources.switch_on;//打开主麦
|
||||
pic_subMicrophone.Image = Properties.Resources.switch_off;//关闭客麦
|
||||
break;
|
||||
case "讨论":
|
||||
pic_lightsStatus.Image = Properties.Resources.switch_on;//转换灯光图片
|
||||
lamp.OnOff(true);//开启界面灯光
|
||||
DeviceCtrlHelper.adam4150Helper.OnOff(1,true);//打开1通道灯
|
||||
trb_curtain.Value = 0;//打开窗帘
|
||||
pic_mainMicrophone.Image = Properties.Resources.switch_on;//打开主麦
|
||||
pic_subMicrophone.Image = Properties.Resources.switch_on;//打开客麦
|
||||
break;
|
||||
case "展示":
|
||||
pic_lightsStatus.Image = Properties.Resources.switch_on;//转换灯光图片
|
||||
lamp.OnOff(true);//开启界面灯光
|
||||
DeviceCtrlHelper.adam4150Helper.OnOff(1, true);//打开1通道灯
|
||||
trb_curtain.Value = 10;//关闭窗帘
|
||||
pic_mainMicrophone.Image = Properties.Resources.switch_on;//打开主麦
|
||||
pic_subMicrophone.Image = Properties.Resources.switch_off;//关闭主麦
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void timer_Tick(object sender, EventArgs e)
|
||||
{
|
||||
DeviceCtrlHelper.adam4017Helper.Open(com_import.Text);
|
||||
DeviceCtrlHelper.adam4150Helper.Open(com_import.Text);
|
||||
now = DateTime.Now;
|
||||
lbl_time.Text = now.ToString("HH:mm:ss");//显示时间
|
||||
ResModel = DeviceCtrlHelper.adam4017Helper.SetData();//采集数据
|
||||
//显示采集到的数据
|
||||
txt_light.Text = ResModel.lightValue;
|
||||
txt_temperature.Text = ResModel.temperatureValue;
|
||||
txt_humidity.Text = ResModel.humidityValue;
|
||||
txt_airPressure.Text = ResModel.airPressureValue;
|
||||
txt_airQuality.Text = ResModel.airQualityValue;
|
||||
//模式判断
|
||||
if (now >= reserve)
|
||||
{
|
||||
if (!IsInitialized)
|
||||
{
|
||||
ChooseMode();//进入选择的模式
|
||||
if (chk_record.Checked)
|
||||
{
|
||||
DeviceCtrlHelper.ipCameraHelper.Open(txt_camera.Text, "admin", "");//初始化视频监控参数
|
||||
DeviceCtrlHelper.ipCameraHelper.Start();//开启视频监控
|
||||
}
|
||||
IsInitialized = true;
|
||||
}
|
||||
if (now.Second == 0 && now.Minute % 5 == 0)//每次分钟数被5整除,秒钟数为00拍照,实现每隔5分钟拍照存储
|
||||
{
|
||||
if (ReadImage != null)
|
||||
{
|
||||
ReadImage.Save(AppDomain.CurrentDomain.BaseDirectory + "Camera/" + DateTime.Now.ToString("yyyyMMdd_HH mm ss") + ".jpg");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void pic_lightsStatus_Click(object sender, EventArgs e)
|
||||
{//电灯可以手动开关
|
||||
if (IsLightsOpen)
|
||||
{
|
||||
lamp.OnOff(false);
|
||||
pic_lightsStatus.Image = Properties.Resources.switch_off;
|
||||
DeviceCtrlHelper.adam4150Helper.OnOff(1, false);//关闭1通道灯
|
||||
IsLightsOpen = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
lamp.OnOff(true);
|
||||
pic_lightsStatus.Image = Properties.Resources.switch_on;
|
||||
DeviceCtrlHelper.adam4150Helper.OnOff(1, true);//关闭1通道灯
|
||||
IsLightsOpen = true;
|
||||
}
|
||||
}
|
||||
|
||||
private void pic_subMicrophone_Click(object sender, EventArgs e)
|
||||
{//客麦可以手动开关
|
||||
if (IssubMicrophoneOpen)
|
||||
{
|
||||
pic_subMicrophone.Image = Properties.Resources.switch_off;
|
||||
IssubMicrophoneOpen = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
pic_subMicrophone.Image = Properties.Resources.switch_on;
|
||||
IssubMicrophoneOpen = true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void btn_exit_Click(object sender, EventArgs e)
|
||||
{
|
||||
timer.Stop();
|
||||
reserve = DateTime.Today.AddDays(1);
|
||||
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();//关闭视频监控
|
||||
|
||||
}
|
||||
|
||||
private void pic_mainMicrophone_Click(object sender, EventArgs e)
|
||||
{//主麦可以手动开关
|
||||
if (IsmainMicrophoneOpen)
|
||||
{
|
||||
pic_mainMicrophone.Image = Properties.Resources.switch_off;
|
||||
IsmainMicrophoneOpen = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
pic_mainMicrophone.Image = Properties.Resources.switch_on;
|
||||
IsmainMicrophoneOpen = true;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace 会议室情景模式
|
||||
{
|
||||
static class Program
|
||||
{
|
||||
/// <summary>
|
||||
/// 应用程序的主入口点。
|
||||
/// </summary>
|
||||
[STAThread]
|
||||
static void Main()
|
||||
{
|
||||
Application.EnableVisualStyles();
|
||||
Application.SetCompatibleTextRenderingDefault(false);
|
||||
Application.Run(new frm_control());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// <auto-generated>
|
||||
// 项目资源(平台素材库)。在"项目资源"里添加或移除,不要手动修改这个文件。
|
||||
// </auto-generated>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace 会议室情景模式.Properties {
|
||||
|
||||
/// <summary>
|
||||
/// 项目资源:Properties.Resources.资源名
|
||||
/// </summary>
|
||||
internal class Resources {
|
||||
|
||||
/// <summary>开关:开</summary>
|
||||
internal static System.Drawing.Bitmap switch_on {
|
||||
get { return global::Sim.Host.ProjectResources.Image("switch_on.png"); }
|
||||
}
|
||||
|
||||
/// <summary>开关:关</summary>
|
||||
internal static System.Drawing.Bitmap switch_off {
|
||||
get { return global::Sim.Host.ProjectResources.Image("switch_off.png"); }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
# 评分规则:书上评价表的功能项。每项单独启动一次程序,按步骤操作,核对结果。
|
||||
# 预约时刻用 clock 拨实训台的虚拟时钟。格式说明见 server/src/grading/checks.ts
|
||||
- title: 能实时显示环境数据
|
||||
steps:
|
||||
- select: { control: com_import, item: COM2 }
|
||||
- board: { light: 640, temperature: 24, airQuality: 88 }
|
||||
- expect: { control: txt_light, text: '640', within: 5s }
|
||||
- expect: { control: txt_temperature, text: '24', within: 5s }
|
||||
- expect: { control: txt_airQuality, text: '88', within: 5s }
|
||||
|
||||
- title: 能根据预约时间控制智能设备
|
||||
steps:
|
||||
- clock: '13:19:54'
|
||||
- select: { control: com_import, item: COM2 }
|
||||
- select: { control: com_mode, item: 讨论 }
|
||||
- text: { control: txt_hour, value: '13' }
|
||||
- text: { control: txt_minute, value: '20' }
|
||||
- text: { control: txt_second, value: '00' }
|
||||
- click: btn_reserve
|
||||
- expect: { relay: 1, on: false, desc: 预约时间(13:20:00)没到时不开灯 }
|
||||
- expect: { control: lbl_mode, contains: 讨论模式, within: 15s, desc: 到了预约时间开启讨论模式 }
|
||||
- expect: { relay: 1, on: true, desc: 讨论模式打开灯光 }
|
||||
- expect: { control: trb_curtain, value: 0, desc: 讨论模式打开窗帘 }
|
||||
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"title": "项目7 任务3 实现智慧会议室",
|
||||
"source": "教材书 p148–153,界面和代码取自配套参考工程;补上书上漏写的填充串口列表。预约时刻:在实训台的时钟上拨时间"
|
||||
}
|
||||