最终还是决定了对这次的温湿度警报项目进行一次复盘,作一次总结,希望这篇总结能够帮到有需要的小白。
一、创建项目
本次项目开发IDE为VS2019社区版,如何下载这里不作说明。安装完成后打开,选择项目模板,这里选择Windows窗体应用(。NET Framework),如下图所示。注意!!别选用.net core版,因为需要设计界面。创建项目我认为就这一个点需要注意。
二、仿真平台的搭建
从技术上说仿真平台上的元器件和实物是没有区别的,想省点钱用仿真平台也是挺好的。首先,搭建WSN,无非就是网关、协调器、传感器等,具体看下图。这里说一点,怎么去看传感器获取到的值。这里分为温湿度传感器和烟雾传感器,在仿真平台上点击传感器的属性(如下图),主要留意的就两点,一是Address,二是Data。Address表示传感器的地址属性,举例:温湿度传感器固定地址为0X30,烟雾的0x40。Data表示返回的状态信息,在温湿度传感器的Data那行,第0、2位表示温湿度的状态,而第1、3位才代表温湿度的数值,在后面我们编写程序时,需要将温湿度的数值拿出来,才好和设定的阈值进行比对,从而发出警报,怎么拿后面再说,然后烟雾的我相信你也已经会看了。这一部分我觉得没啥好说的,就知道上面的内容也差不多了。
三、项目界面设计
我计划需要用到两个界面。主界面(如下第一张图)。连接仿真平台上的WSN,这需要获取到你本机的地址,仿真平台上的IP和端口,这里需要用到一个接口,这个接口已经封装好,直接用就可以。只有与仿真平台上的WSN连接后才能开展之后的工作。
这个界面的设计思路:首先和仿真平台上的WSN连接,通过listview控件显示信息,信息包括了是否连接成功,ping指令是否可行等;随后要将获取到的传感器的数值和我们设定的阈值相比对,不符合情况下就发出警报。主界面的代码:
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.Net;
using KV_WSN;
using KV_WSN.Sensor;
using System.Threading;
using System.IO;//此次须引用的命名空间
using System.Xml.Linq;//此次须引用的命名空间
using System.Data.SqlClient;
namespace Graduation_Project_10
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
SensorInfo ui = new SensorInfo();
#region 字段
/// <summary>
/// 网关对象
/// </summary>
public GateWay gateWay = new GateWay();
/// <summary>
/// 显示消息的委托
/// </summary>
/// <param name="msg"></param>
private delegate void ShowMessageDel(string msg);
/// <summary>
/// 设备信息缓存
/// </summary>
public List<SensorBase> TH = new List<SensorBase>();
private bool isCheck = true;//检测传感器
///<summary>
///自定义缓存值
/// </summary>
private string Temp1 = "0";
private string Temp2 = "0";
private string Humi1 = "0";
private string Humi2 = "0";
/// <summary>
/// 定义设定阈值
/// </summary>
private string strMaxTemp = "30";
private string strMinTemp = "20";
private string strMaxHumi = "0.80";
private string strMinHumi = "0.20";
/// <summary>
/// 图片委托
/// </summary>
private delegate void ShowPictureBoxDel(PictureBox pictureBox, bool visible);
private delegate void ShowLabelDel(Label label, string message, bool visible);
#endregion
private void Form1_Load(object sender, EventArgs e)
{
ui.OpenSqlConnection();
try
{
String strHostName = Dns.GetHostName();
IPHostEntry ipEntry = Dns.GetHostByName(strHostName);
txt_IPAddr.Text = ipEntry.AddressList[0].ToString();
}
catch (Exception err)
{
this.txt_IPAddr.Text = "";
ShowMessage(err.Message);
}
}
private void btn_Connect_Click(object sender, EventArgs e)
{
gateWay.ReceiveSensorEvent += GateWay_EventDataArrival;
//连接网关执行语句,传入IP地址和端口
if (gateWay.Connect(txt_IPAddr.Text.Trim(), int.Parse(txt_IPPort.Text.Trim())))
{
ShowMessage("建立网关连接成功!");
}
else
{
ShowMessage("建立网关连接失败!");
}
}
private void btn_DisConnect_Click(object sender, EventArgs e)
{
//断开网关连接
if (gateWay.DisConnect())
{
ShowMessage("断开网关连接成功!");
}
else
{
ShowMessage("断开网关连接失败!");
}
}
private void btn_Ping_Click(object sender, EventArgs e)
{
gateWay.Ping();
Thread.Sleep(500);
if (isCheck == false)
{
ShowMessage("发送Ping指令失败!");
}
else
{
ShowMessage("发送Ping指令成功!");
}
}
/// <summary>
/// 在ListView上显示信息
/// </summary>
/// <param name="msg"></param>
private void ShowMessage(string msg)
{
if (lv_Message.InvokeRequired)
{
ShowMessageDel d = new ShowMessageDel(ShowMessage);
this.Invoke(d, msg);
}
else
{
ListViewItem item = new ListViewItem();
item.Text = DateTime.Now.ToString();
item.SubItems.Add(msg);
lv_Message.Items.Insert(0, item);
}
}
/// <summary>
/// 匹配类型
/// </summary>
void GateWay_EventDataArrival(List<SensorBase> sensorList)
{
foreach (SensorBase sensor in sensorList)
{
if (sensor.Type == 0x30)
{
isCheck = true;
SensorBase s = TH.FirstOrDefault(p => p.Type.Equals(sensor.Type) && p.Addr.Equals(sensor.Addr));
if (s == null)
{
TH.Add(sensor);
ShowMessage(string.Format("寻到温湿度传感器节点,地址为:{0}", Converts.ByteToString(sensor.Addr)));
}
}
}
foreach (SensorBase sensor in sensorList)
{
if (sensor.Type == 0x40)
{
isCheck = true;
SensorBase a = TH.FirstOrDefault(p => p.Type.Equals(sensor.Type) && p.Addr.Equals(sensor.Addr));
if (a == null)
{
TH.Add(sensor);
ShowMessage(string.Format("寻到烟雾传感器节点,地址为:{0}", Converts.ByteToString(sensor.Addr)));
}
}
}
foreach (SensorBase th in TH)
{
for (int i = 0; i < TH.Count; i++)
{
gateWay.SendData(TH[i].Type, TH[i].Addr, new byte[] { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF });
ShowMessage(string.Format("节点{0}的信息:{1}", i, TH[i].GetData()));
Thread.Sleep(1000);
}
}
}
private void groupBox1_Enter(object sender, EventArgs e)
{
}
private void btnSaveSetting_Click(object sender, EventArgs e)
{
strMaxTemp = txtMaxTemp.Text.Trim();
strMinTemp = txtMinTemp.Text.Trim();
strMaxHumi = txtMaxHumi.Text.Trim();
strMinHumi = txtMinHumi.Text.Trim();
}
/// <summary>
/// 发送数据
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btn_AcquireTempAndHumi_Click_1(object sender, EventArgs e)
{
if (btn_AcquireTempAndHumi.Text == "获取温湿度")
{
lb_TempHumi.Text = "";
for (int i = 0; i < TH.Count; i++)
{
gateWay.SendData(TH[i].Type, TH[i].Addr, new byte[] { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF });
Thread.Sleep(10);
ShowMessage(string.Format("温湿度传感器节点{0}数据获取成功!", i));
lb_TempHumi.Text = lb_TempHumi.Text + TH[i].GetData() + "\n";
}
btn_AcquireTempAndHumi.Text = "更新温湿度";
}
else
{
isCheck = false;
btn_AcquireTempAndHumi.Text = "获取温湿度";
}
}
/// <summary>
/// 显示消息到Label控件
/// </summary>
/// <param name="lbl">Label控件的名称</param>
/// <param name="msg">消息内容</param>
/// <param name="visible">Label控件的Visible属性值</param>
private void ShowLabel(Label lbl, string msg, bool visible)
{
if (lbl.InvokeRequired)
{
ShowLabelDel sld = new ShowLabelDel(ShowLabel);
this.Invoke(sld, lbl, msg, visible);
}
else
{
lbl.Visible = visible;
lbl.Text = msg;
}
}
/// <summary>
/// 设置PictureBox控件的Visible属性
/// </summary>
/// <param name="pic">PictureBox控件的名称</param>
/// <param name="visible">PictureBox控件的Visible属性值</param>
private void ShowPictureBox(PictureBox pic, bool visible)
{
if (pic.InvokeRequired)
{
ShowPictureBoxDel spd = new ShowPictureBoxDel(ShowPictureBox);
this.Invoke(spd, pic, visible);
}
else
{
pic.Visible = visible;
}
}
private void button1_Click(object sender, EventArgs e)
{
foreach (SensorBase th in TH) //2次循坏TH.Count2
{
if (th.Type == 0x30)
{
if (Convert.ToInt16(th.Addr).ToString() == "1")
{
Temp[0] = Convert.ToInt16(th.Data[1]).ToString();
Humi[0] = Convert.ToInt16(th.Data[3]).ToString();
Temp1 = Temp[0];
Humi1 = Humi[0];
Thread.Sleep(10);
ui._Tag = "3001";
ui._Name = "A节点";
ui._Tempdata = Temp1;
ui._Humidata = Humi1;
ui._Timing = DateTime.Now.ToString();
if (Convert.ToDouble(Temp1) < Convert.ToDouble(strMaxTemp) || Convert.ToDouble(Temp1) > Convert.ToDouble(strMaxTemp))
{
ui._State = "A节点警报:温度异常";
ui.Insert();
}
if (Convert.ToDouble(Humi1) < Convert.ToDouble(strMaxHumi) || Convert.ToDouble(Humi1) > Convert.ToDouble(strMaxHumi))
{
ui._State = "A节点警报:湿度异常";
ui.Insert();
}
}
if (Convert.ToInt16(th.Addr).ToString() == "2")
{
Temp[1] = Convert.ToInt16(th.Data[1]).ToString();
Humi[1] = Convert.ToInt16(th.Data[3]).ToString();
Temp2 = Temp[1];
Humi2 = Humi[1];
Thread.Sleep(10);
ui._Tag = "3002";
ui._Name = "B节点";
ui._Tempdata = Temp2;
ui._Humidata = Humi2;
ui._Timing = DateTime.Now.ToString();
if (Convert.ToDouble(Temp2) < Convert.ToDouble(strMaxTemp) || Convert.ToDouble(Temp2) > Convert.ToDouble(strMaxTemp))
{
ui._State = "B节点警报:温度异常";
ui.Insert();
}
if (Convert.ToDouble(Humi2) < Convert.ToDouble(strMaxHumi) || Convert.ToDouble(Humi2) > Convert.ToDouble(strMaxHumi))
{
ui._State = "B节点警报:湿度异常";
ui.Insert();
}
}
}
}
Thread thread1 = new Thread(new ThreadStart(Check1));
Thread thread2 = new Thread(new ThreadStart(Check2));
thread2.IsBackground = true;
thread1.IsBackground = true;
thread1.Start();
thread2.Start();
}
#region 监测方法 Check1 Check2
private void Check1()
{
while (true)
{
try
{
SqlConnection SqlConn = new SqlConnection(" ");
Thread.Sleep(100);
/*
节点 1 温度 Temp1
*/
if (Convert.ToDouble(Temp1) > Convert.ToDouble(strMaxTemp))
{
ShowLabel(lblTemp, "A节点温度太高", true);
ShowPictureBox(picTempAndHumi, true);
}
else if (Convert.ToDouble(Temp1) < Convert.ToDouble(strMinTemp))
{
ShowLabel(lblTemp, "A节点温度太低", true);
ShowPictureBox(picTempAndHumi, true);
}
else
{
ShowLabel(lblTemp, "", false);
if (Convert.ToDouble(Humi1) <= Convert.ToDouble(strMaxHumi) && Convert.ToDouble(Humi1) >= Convert.ToDouble(strMinHumi))
{
ShowPictureBox(picTempAndHumi, false);
}
}
/*
节点 1 湿度 Humi1
*/
if (Convert.ToDouble(Humi1) > Convert.ToDouble(strMaxHumi))
{
ShowLabel(lblHumi, "A节点湿度太高", true);
ShowPictureBox(picTempAndHumi, true);
}
else if (Convert.ToDouble(Humi1) < Convert.ToDouble(strMinHumi))
{
ShowLabel(lblHumi, "A节点湿度太低", true);
ShowPictureBox(picTempAndHumi, true);
//ui._State = lblHumi.Text;
//ui.Insert1();
}
else
{
ShowLabel(lblHumi, "", false);
if (Convert.ToDouble(Temp1) <= Convert.ToDouble(strMaxTemp) && Convert.ToDouble(Temp1) >= Convert.ToDouble(strMinTemp))
{
ShowPictureBox(picTempAndHumi, false);
}
}
}
catch
{
}
}
}
private void Check2()
{
while (true)
{
try
{
Thread.Sleep(100);
/*
节点 2 温度 Temp2
*/
if (Convert.ToDouble(Temp2) > Convert.ToDouble(strMaxTemp))
{
ShowLabel(lalTemp, "B节点温度太高", true);
ShowPictureBox(picTempAndHumi, true);
}
else if (Convert.ToDouble(Temp2) < Convert.ToDouble(strMinTemp))
{
ShowLabel(lalTemp, "B节点温度太低", true);
ShowPictureBox(picTempAndHumi, true);
}
else
{
ShowLabel(lalTemp, "", false);
if (Convert.ToDouble(Humi2) <= Convert.ToDouble(strMaxHumi) && Convert.ToDouble(Humi2) >= Convert.ToDouble(strMinHumi))
{
ShowPictureBox(picTempAndHumi, false);
}
}
/*
节点 2 湿度 Humi2
*/
if (Convert.ToDouble(Humi2) > Convert.ToDouble(strMaxHumi))
{
ShowLabel(lalHumi, "B节点湿度太高", true);
ShowPictureBox(picTempAndHumi, true);
}
else if (Convert.ToDouble(Humi2) < Convert.ToDouble(strMinHumi))
{
ShowLabel(lalHumi, "B节点湿度太低", true);
ShowPictureBox(picTempAndHumi, true);
}
else
{
ShowLabel(lalHumi, "", false);
if (Convert.ToDouble(Temp2) <= Convert.ToDouble(strMaxTemp) && Convert.ToDouble(Temp2) >= Convert.ToDouble(strMinTemp))
{
ShowPictureBox(picTempAndHumi, false);
}
}
}
catch
{
}
}
}
#endregion
#region 添加界面跳转
public void button2_Click(object sender, EventArgs e)
{
FormTH ab = new FormTH();
ab.Show(this);
}
#endregion
#region 查询界面跳转
private void UIskip2_Click(object sender, EventArgs e)
{
FormRecord ac = new FormRecord();
ac.Show(this);
}
#endregion
}
}
我必须承认一点,上面的代码显得过于稚嫩!但我相信这样对于小白会好理解。(实际上是个人能力不行!)
之前有提到,怎么将传感器的温度和湿度单独拿出来呢?一开始先定义一个用于缓存信息的列表,用于缓存传感器的信息,这里的传感器是包括在你组建的WSN内的所有传感器,通过TH.Add()方式进行添加。若想单独拿温度值出来,即拿传感器属性Data中的第1号位,然后再进行字符类型转换。湿度同理,即第3号位。
public List<SensorBase> TH = new List<SensorBase>();
Convert.ToInt16(th.Data[1]).ToString();
Convert.ToInt16(th.Data[3]).ToString();
当拿到的温湿度数值和设定的阈值范围不匹配,就将其视作一个事件,将这个事件存进数据库中。第二个界面就是查询这个事件的。那么从主界面跳转到查询界面如何实现?这本质就是窗体之间的跳转。在主界面上添加一个button控件,然后点击进入到代码设计。FormRecord:你要跳转到的界面名称;ac:可随意命名(如果你仅仅为了实现跳转这个功能)。
FormRecord ac = new FormRecord();
ac.Show(this);
四、数据库
需要新建一个类,实现的功能:获取数据库的字符串,将信息存到表中。这里我只用到一个表,有6个键,分别为传感器地址、传感器名称、温度、湿度、警报状态和警报时间。注意!在是否允许NULL值那栏你得有所考虑,当你不允许NULL值,意味着你插入数据时该列名必须非null。举例:表内有3个键,分别为账号、姓名和密码,一般来说我们想注册成功都得要填写账号、姓名和密码。若有一个无名氏,他没有名字怎么办?难道就因为没有名字就不可以注册,或说他得随意编造一个名字,其实这时只需要将姓名设置为允许null值便可以。这其实和我们插入温湿度数据是一样的,警报状态和警报时间我设定允许null值,在没有警报发生时数据才可以插进去。类的代码:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;
using System.Data;
namespace Graduation_Project_10
{
class SensorInfo
{
public string _Tag;
public string _Name;
public string _Humidata;
public string _Tempdata;
public string _State;
public string _Timing;
public static SqlServer_dll.DbConnection dbc;
public static SqlServer_dll.DbOperate dbo;
/// <summary>
/// 获取数据库配置字符串
/// </summary>
/// <returns></returns>
public static string GetConnectionString()
{
string strServer = AppDomain.CurrentDomain.BaseDirectory + "SqlConfig.txt";
return File.ReadAllText(strServer);
}
/// <summary>
/// sensor_address
/// </summary>
public string Tag
{
get
{
return _Tag;
}
set
{
_Tag = value;
}
}
public string Name
{
get
{
return _Name;
}
set
{
_Name = value;
}
}
public string Humidata
{
get
{
return _Humidata;
}
set
{
_Humidata = value;
}
}
public string Tempdata
{
get
{
return _Tempdata;
}
set
{
_Tempdata = value;
}
}
/// <summary>
/// sensor_alram
/// </summary>
public string State
{
get
{
return _State;
}
set
{
_State = value;
}
}
/// <summary>
/// 时间
/// </summary>
public string Timing
{
get
{
return _Timing;
}
set
{
_Timing = value;
}
}
public SensorInfo()
{
}
private void SetObject(DataRow row)
{
this.Tag = row["sensor_address"].ToString();
this.Name = row["sensor_name"].ToString();
this.Humidata = row["humi_data"].ToString();
this.Tempdata = row["temp_data"].ToString();
this.State = row["sensor_alarm"].ToString();
this.Timing = row["sensor_time"].ToString();
}
public bool OpenSqlConnection()
{
try
{
dbc = new SqlServer_dll.DbConnection(GetConnectionString());
dbc.DbOpen();
SensorInfo.dbo = new SqlServer_dll.DbOperate(dbc);
}
catch
{
return false;
}
return true;
}
public bool SelectRegister(string Tag)
{
string sql = string.Format("SELECT * FROM SensorAlarm WHERE sensor_address = '{0}' ", Tag);
DataTable dt = dbo.GetDataTable(sql);
if (dt.Rows.Count > 0)
{
return true;
}
else
{
return false;
}
}
public void SelectInfo(string sensor_address)
{
string sql = string.Format("SELECT * FROM SensorAlarm WHERE sensor_address = '{0}'", sensor_address);
DataTable dt = dbo.GetDataTable(sql);
if (dt.Rows.Count > 0)
{
SetObject(dt.Rows[0]);
}
}
public void Insert()
{
string Sql = string.Format("INSERT INTO SensorAlarm (sensor_address,sensor_name,humi_data,temp_data, sensor_alarm, sensor_time) VALUES ('{0}','{1}','{2}','{3}','{4}','{5}')", _Tag, _Name, _Humidata, _Tempdata, _State, _Timing);
dbo.ExcuteSql(Sql);
}
public void Update()
{
string sql = string.Format("UPDATE PersonInfo set permission='{0}' WHERE cardID='{1}'", _State, _Tag);
dbo.ExcuteSql(sql);
}
public void Delete()
{
string sql = string.Format("DELETE FROM SensorAlarm WHERE sensor_address='{0}'", _Tag);
dbo.ExcuteSql(sql);
}
}
}
假设我们将数据存到了数据库,那如何进行查询呢?首先要判定数据库里有没有这样一条数据。这语句意思:在表SensorAlarm中查询sensor_adderss值为xxxx,(这个xxxx是你输入的信息)并将查询到的结果给Dataset中的表里,如果数据库里有那么一条数据,Dataset中的datatable就会有那么一行,这就是dt.Rows.Count > 0的意思,if{}内则填上你之后要实现的功能。(补充一句:实现用户登录时检验用户名和密码和数据库中的是否一致也是这个思路,代码也基本一致)
string sql = string.Format("SELECT * FROM SensorAlarm WHERE sensor_address = '{0}'", sensor_address);
DataTable dt = dbo.GetDataTable(sql);
if (dt.Rows.Count > 0)
{
}
大概就是这么一个过程吧,嗯,就这样吧,有啥问题或想法可以留言。
上述思路或说法有偏颇或的不对的请各位大牛直接指出来,万分感谢!!!