概述
这个程序是本科课程设计写的程序,因为最近在复习java和C#,故把以前的东西拿出来看看,顺便写个博客。
此程序的功能描述:通过上位机可以直接观察到温室的各项实时参数和各个装置的工作状态,并且可以通过曲线图直观地分析该温室最近一段时间的环境变化。上位机可对终端中遮阳、风机等装置进行远程控制。管理员可以通过查询数据库来查看温室的历史环境参数。上位机将采集数据转发至移动终端,方便管理员能够随时随地查看温室内情况,同时,上位机可接受移动终端下达的指令并通过串口转发至底层终端。为了使主进程能够流畅的运行,不出现卡顿的情况,我使用线程池,为每一个模块执行的重要操作都分配一个线程,这样便可以保证程序不再运行过程中会出现无响应等异常情况。整个程序包括自动报警、串口通信、图表显示、数据存储、网络通信等主要模块。
主要的模块就三个:
1.串口通信的上传下达(与底层硬件)
2.网络通信(与APP)
3.数据的显示(图表、数据库、报表)
一个一个来看:
1.串口通信
上位机服务器与底层终端的通信采用串口来实现,由.NET Framework提供的操作串口的System.IO.Port.SerialPort类创建并初始化对象PortNameArr,调用GetPortNames()方法获取目标串口名称,并通过主窗体上的ComboBox控件配置波特率、数据位等主要参数,通过注册监听方式接收数据,关键代码如下:
if (pchReceive.PortState) //检查串口当前状态
{
pchReceive.ClosePort();
}
else
{
pchReceive.OpenPort(cb_portNameReceive.Text, int.Parse(cb_baudRate.Text),//打开控件内所选择的串口
int.Parse(cb_dataBit.Text), int.Parse(cb_stopBit.Text),int.Parse(cb_timeout.Text));
}
FreshBtnState(pchReceive.PortState);//更新串口状态
pchReceive.OnComReceiveDataHandler+=new PortControlHelper.//创建串口接收线程
ComReceiveDataHandler(ComReceiveData);//接收线程调用接收函数
Btn_receive.Text = "停止接收";
ReceiveState = true;
效果如图:
通过串口助手将字符串传到上位机,上位机通过按位截取的方式在对应文本框显示数据。
上位机通过串口下达指令,前面四位为温度上下限,0代表每一个电机为关闭状态。
2.网络通信
上位机就用户设置的本地IP地址和端口创建一个TCP服务器套接字,并调用Bind()方法绑定IP和端口,调用Listen()方法监听,之后再创建一个线程用来监听安卓TCP客户端到PC端的连接。
PC端上位机服务器监听线程流程图如图3-3所示。线程开始后,循环执行如下操作:通过调用TCP服务器套接字的Accept()方法等待安卓客户端连接到PC端,当安卓端成功通过TCP连接上PC端后,Accept方法返回客户端的套接字。之后,创建一个TCP接收线程用来接收安卓端发送给PC端的数据。关键代码如下:
Socket sokConnection = socketWatch.Accept();//监听套接字
lbOnline.Items.Add(sokConnection.RemoteEndPoint.ToString());// 向列表控件中添加客户端的IP信息
dict.Add(sokConnection.RemoteEndPoint.ToString(), sokConnection); // 将与客户端连接的套接字对象添加到集合中
ShowMsg(shijian + "客户端连接成功!");
Socket sokClient = sokConnectionparn as Socket;
byte[] arrMsgRec = new byte[1024 * 1024 * 2];//定义缓存区
int length = -1;
length = sokClient.Receive(arrMsgRec); //接收数据
string strMsg = System.Text.Encoding.UTF8.GetString(arrMsgRec, 0, length); // 将接受到的字节数据转化成字符串
strMsg = strMsg + "n";
Doupdate(strMsg);
这个效果在APP的博客里展示。
3.数据显示
(1)数据显示
数据显示模块包含数据实时显示部分和图表显示部分,实时显示即在极短时间内系统对终端传送的数据及时接收并显示,图表显示则对接收的数据在坐标系上进行打点,生动直观的呈现给用户。上位机与终端进行连接通信后,终端将采集到的温度、湿度、光照强度等参数上传至上位机,上位机将实时接收到的数据显示在图表曲线的最右边,随着数据的不断更新,图表曲线随之动态左移。
本设计在数据显示模块中所使用的的控件有ComboBox、TextBox、RichTextBox、ListBox和微软免费提供的.NET Framework 3.5图表控件。串口数据接收线程每隔2000ms抓取一次数据,将所接收的字节数组转换为字符串,并调用Substring(a,b)函数将字符串按所定通信协议进行按位截取,将所截取的字符串赋值到对应的TextBox控件上进行实时显示。图表显示则需将对应传感器数据先转换成整型,再通过AddPoint(chart,int)函数将当前数据添加到图表中。
(2)数据存储
数据存储模块主要功能就是将上位机解析过后的数据存入数据库中。我创建一个新线程用来对数据库进行数据更新,当上位机与底层终端连接成功后便启动数据库存储线程,每当上位机接收并解析一次数据时,该线程便把该数据和它的接收时间保存于字符数组中。由于上位机接收来自底层终端传送的数据后,需要对该条数据进行解析、显示、存储等多个操作,对此,我新建另一个线程用来实现对数据的存储功能,还设置一个全局变量用于对数据进行暂时存储,以便访问线程。
(3)数据查询
数据查询模块可根据用户设置的查询条件和查询类型在数据库中查找对应的数据并显示在对话框表格中。查询类型分为时间查询和内容查询。内容查询根据温室编号查询某一参数。时间查询则是根据对话框中输入某一个时间点和某一兰花温室大棚编号,然后返回在该时间点的温湿度和光照强度等参数。该模块也可以进行时间查询和内容查询的联合查询。建立相应的信息查询对话框,并在其上添加设置相关的控件。
效果如图:
代码在后面放一起。
代码:
namespace 温室监控系统
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
CheckForIllegalCrossThreadCalls = false;
pchSend = new PortControlHelper();
pchReceive = new PortControlHelper();
String hostName = Dns.GetHostName();
IPHostEntry iPHostEntry = Dns.GetHostEntry(hostName);
foreach (IPAddress ipAddress in iPHostEntry.AddressList)
{
if (ipAddress.AddressFamily == AddressFamily.InterNetwork)
{
cbLocalIP.Items.Add(ipAddress.ToString());
}
}
cbLocalIP.SelectedIndex = 0;
InitView();
InitChart(charttwen, "土壤温度", Color.Blue, 0, 100);
InitChart(charttshi, "土壤湿度", Color.Red, 0, 100);
InitChart(charthnwen, "环境温度", Color.Green, 0, 100);
InitChart(charthshi, "环境湿度", Color.Tomato, 0, 100);
InitChart(chartguangqiang, "光照强度", Color.Pink, 0, 10000);
InitChart(chartdianliu, "电流", Color.Yellow, 0, 100);
ID.Text = "room1";
this.waizheyangshang_bt.SelectedIndex = 0;
this.waizheyangxia_bt.SelectedIndex = 0;
this.dingkaichuang_bt.SelectedIndex = 0;
this.cekaichuang_bt.SelectedIndex = 0;
this.shilianfengji_bt.SelectedIndex = 0;
this.zhouliufengji_bt.SelectedIndex = 0;
this.huanliufengji_bt.SelectedIndex = 0;
this.wuhua_bt.SelectedIndex = 0;
this.guangai_bt.SelectedIndex = 0;
}
private void InitView()
{
cb_portNameReceive.DataSource = pchReceive.PortNameArr;
cb_baudRate.DataSource = BaudRateArr;
cb_dataBit.DataSource = DataBitArr;
cb_stopBit.DataSource = StopBitArr;
cb_checkBit.DataSource = CheckBitArr;
cb_timeout.DataSource = TimeoutArr;
FreshBtnState(pchReceive.PortState);
}
private void FreshBtnState(bool state)
{
if (state)
{
Btn_open.Text = "关闭接收串口"
Btn_receive.Enabled = true;
}
else
{
Btn_open.Text = "打开接收串口";
Btn_receive.Enabled = false;
}
}
private void ComReceiveData(string data)
{
this.Invoke(new EventHandler(delegate
{
tb_receive.AppendText(data);
portstring = data;
}));
}
private void Btn_open_Click(object sender, EventArgs e)
{
if (pchReceive.PortState)
{
pchReceive.ClosePort();
}
else
{
pchReceive.OpenPort(cb_portNameReceive.Text, int.Parse(cb_baudRate.Text),
int.Parse(cb_dataBit.Text), int.Parse(cb_stopBit.Text),
int.Parse(cb_timeout.Text));
}
FreshBtnState(pchReceive.PortState);
pchReceive.OnComReceiveDataHandler += new PortControlHelper.ComReceiveDataHandler(ComReceiveData);
Btn_receive.Text = "停止接收";
ReceiveState = true;
}
private void Btn_receive_Click(object sender, EventArgs e)
{
if (ReceiveState)
{
pchReceive.OnComReceiveDataHandler -= new PortControlHelper.ComReceiveDataHandler(ComReceiveData);
Btn_receive.Text = "开始接收";
ReceiveState = false;
}
else
{
pchReceive.OnComReceiveDataHandler += new PortControlHelper.ComReceiveDataHandler(ComReceiveData);
Btn_receive.Text = "停止接收";
ReceiveState = true;
}
}
void WatchConnecting()
{
string shijian = System.DateTime.Now.ToString();
while (true)
{
lbOnline.Items.Add(sokConnection.RemoteEndPoint.ToString()); dict.Add(sokConnection.RemoteEndPoint.ToString(), sokConnection);
ShowMsg(shijian + " 客户端连接成功!");
Thread thr = new Thread(RecMsg);
thr.IsBackground = true;
thr.Start(sokConnection);
dictThread.Add(sokConnection.RemoteEndPoint.ToString(), thr);
}
}
void RecMsg(object sokConnectionparn)
{
Socket sokClient = sokConnectionparn as Socket;
while (true)
{
byte[] arrMsgRec = new byte[1024 * 1024 * 2];
int length = -1;
try
{
length = sokClient.Receive(arrMsgRec);
}
catch (SocketException se)
{
ShowMsg("异常:" + se.Message);
dict.Remove(sokClient.RemoteEndPoint.ToString());
dictThread.Remove(sokClient.RemoteEndPoint.ToString()); lbOnline.Items.Remove(sokClient.RemoteEndPoint.ToString());
break;
}
catch (Exception e)
{
ShowMsg("异常:" + e.Message);
dict.Remove(sokClient.RemoteEndPoint.ToString());
dictThread.Remove(sokClient.RemoteEndPoint.ToString());
lbOnline.Items.Remove(sokClient.RemoteEndPoint.ToString());
break;
}
string strMsg = System.Text.Encoding.UTF8.GetString(arrMsgRec, 0, length);
strMsg = strMsg + "n";
Doupdate(strMsg);
}
}
void ShowMsg(string str)
{
richTextBox2.AppendText(str + "rn");
}
void send()
{
string strMsg = mystring1.ToString();
byte[] arrMsg = System.Text.Encoding.UTF8.GetBytes(strMsg);
foreach (Socket s in dict.Values)
{
s.Send(arrMsg);
}
}
private void GetMess()
{
while (true)
{
string result3 = portstring;
double[][] Five_data = new double[1][];
Five_data[0] = new double[7];
string[] TimeStamp = new string[1];
mystring1 = result3 + "n";
string Tu_wendu= result3.Substring(1, 2);
string Tu_shidu = result3.Substring(3, 2);
string Huan_wendu = result3.Substring(5, 2);
string Huan_shidu = result3.Substring(7, 2);
string Guangqiang = result3.Substring(9, 4);
string Dianliu = result3.Substring(13, 2);
waizheyang_state = result3.Substring(15, 1);
neizheyang_state = result3.Substring(16, 1);
dingkaichuang_state = result3.Substring(17, 1);
cekaichuang_state = result3.Substring(18, 1);
shilian_state = result3.Substring(19, 1);
zhouliufengji_state = result3.Substring(20, 1);
huanliufengji_state = result3.Substring(21, 1);
wuhua_state = result3.Substring(22, 1);
guangai_state = result3.Substring(23, 1);
send();
this.turangwendu.Text = Tu_wendu;
this.turangshidu.Text = Tu_shidu;
this.huanjingwendu.Text = Huan_wendu;
this.huanjingshidu.Text = Huan_shidu;
this.guangqiang.Text = Guangqiang;
this.huoerdianliu.Text = Dianliu;
UpdateQueueValue();
int inttuwendu = int.Parse(Tu_wendu);
int inttushidu = int.Parse(Tu_shidu);
int inthuanwendu = int.Parse(Huan_wendu);
int inthuanshidu = int.Parse(Huan_shidu);
int intguangqiang = int.Parse(Guangqiang);
int intdianliu = int.Parse(Dianliu);
AddPoint(charttuwen, inttuwendu);
AddPoint(charttushi, inttushidu);
AddPoint(charthuanwen, inthuanwendu);
AddPoint(charthuanshi, inthuanshidu);
AddPoint(chartguangqiang, intguangqiang);
AddPoint(chartdianliu, intdianliu);
yuzhiwenduxia = Yuzhi_xia_wendu.Text.ToString().Trim();
yuzhiwendushang = Yuzhi_shang_wendu.Text.ToString().Trim();
if (inthuanwendu< int.Parse(yuzhiwenduxia))
{
richTextBox2.AppendText("当前温室温度低于阈值rn");
}
if (inthuanwendu > int.Parse(yuzhiwendushang))
{
richTextBox2.AppendText("当前温室温度高于阈值rn");
}
if (intdianliu==0)
{
richTextBox2.AppendText("温室停电!rn");
}
string shebeihao="0";
if (ID.Text.ToString().Trim()!=null)
shebeihao= ID.Text.ToString().Trim();
string sqltuwendu = Tu_wendu + "℃";
string sqltushidu = Tu_shidu + "%";
string sqlhuanwendu = Huan_wendu + "℃";
string sqlhuanshidu = Huan_shidu + "%";
string sqlquangqiang = Guangqiang + "Lux";
string sqldianliu = Dianliu + "A";
string shijian = System.DateTime.Now.ToString();
AddRecord(shebeihao, sqltuwendu, sqltushidu, sqlhuanwendu, sqlhuanshidu, sqlquangqiang, sqldianliu, shijian);
DispDatabase();
Thread.Sleep(2000);
}
}
private void button1_Click(object sender, EventArgs e)
{
string shijian = System.DateTime.Now.ToString();
socketWatch = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
IPAddress address = IPAddress.Parse(cbLocalIP.Text.Trim());
IPEndPoint endPoint = new IPEndPoint(address, int.Parse(COM.Text.Trim()));
String sqlStr = "server='.\SQLy';" +
"database='Enmo';" +
"uid='sa';" +
"pwd='123456';";
sqlConnection = new SqlConnection(sqlStr);
sqlConnection.Open();
ClearRecord();
DispDatabase();
try
{
socketWatch.Bind(endPoint);
}
catch (SocketException se)
{
MessageBox.Show("异常:" + se.Message);
return;
}
socketWatch.Listen(10);
threadWatch = new Thread(WatchConnecting);
threadWatch.IsBackground = true;
threadWatch.Start();
getmess = new Thread(GetMess);
getmess.IsBackground = true;
getmess.Start();
ShowMsg(shijian + " 服务器启动监听成功!");
button1.Text = "关闭";
}
private void 退出ToolStripMenuItem_Click(object sender, EventArgs e)
{
sqlConnection.Close();
sqlConnection.Dispose();
this.Close();
}
private void 重启ToolStripMenuItem_Click(object sender, EventArgs e)
{
if (MessageBox.Show("要重新启动吗?", "提示", MessageBoxButtons.YesNoCancel,
MessageBoxIcon.Question) == DialogResult.Yes)
{ System.Diagnostics.Process.Start(System.Reflection.Assembly.GetExecutingAssembly().Location);
System.Environment.Exit(0);
}
}
private void button4_Click(object sender, EventArgs e)
{
yuzhiwenduxia = Yuzhi_xia_wendu.Text.ToString().Trim(); //2
yuzhiwendushang = Yuzhi_shang_wendu.Text.ToString().Trim(); //2
if (waizheyangshang_bt.Text=="关")
{
waizheyang_order = "0";
}
else if (waizheyangshang_bt.Text == "开")
{
waizheyang_order = "1";
}
if (waizheyangxia_bt.Text == "关")
{
neizheyang_order = "0";
}
else if (waizheyangxia_bt.Text == "开")
{
neizheyang_order = "1";
}
if (dingkaichuang_bt.Text == "关")
{
dingkaichuang_order = "0";
}
else if (dingkaichuang_bt.Text == "开")
{
dingkaichuang_order = "1";
}
if (cekaichuang_bt.Text == "关")
{
cekaichuang_order = "0";
}
else if (cekaichuang_bt.Text == "开")
{
cekaichuang_order = "1";
}
if (shilianfengji_bt.Text == "关")
{
shilian_order = "0";
}
else if (shilianfengji_bt.Text == "开")
{
shilian_order = "1";
}
if (zhouliufengji_bt.Text == "关")
{
zhouliufengji_order = "0";
}
else if (zhouliufengji_bt.Text == "开")
{
zhouliufengji_order = "1";
}
if (huanliufengji_bt.Text == "关")
{
huanliufengji_order = "0";
}
else if (huanliufengji_bt.Text == "开")
{
huanliufengji_order = "1";
}
if (wuhua_bt.Text == "关")
{
wuhua_order = "0";
}
else if (wuhua_bt.Text == "开")
{
wuhua_order = "1";
}
if (guangai_bt.Text == "关")
{
guangai_order = "0";
}
else if (guangai_bt.Text == "开")
{
guangai_order = "1";
}
string order = "";
order = "#" + yuzhiwenduxia + yuzhiwendushang + waizheyang_order + neizheyang_order + dingkaichuang_order + cekaichuang_order + shilian_order+ zhouliufengji_order+ huanliufengji_order+ wuhua_order+ guangai_order;
pchReceive.SendData(order);
richTextBox2.AppendText("指令下达成功rn");
}
private void InitChart(Chart chart, String title, Color color, int minY, int maxY)
{
Series series = chart.Series[0];
series.ChartType = SeriesChartType.FastLine;
series.BorderWidth = 2;
series.Color = color;
chart.Legends[0].Enabled = false;
chart.Titles.Clear();
chart.Titles.Add(title);
chart.Titles[0].Text = title;
ChartArea chartArea = chart.ChartAreas[0];
chartArea.AxisX.Minimum = 0;
chartArea.AxisY.Minimum = minY;
chartArea.AxisY.Maximum = maxY;
chartArea.AxisX.ScrollBar.IsPositionedInside = false;
chartArea.AxisX.ScrollBar.Enabled = true;
chartArea.AxisX.ScaleView.Position = 0;
chartArea.AxisX.LabelStyle.ForeColor = Color.White;
chartArea.AxisX.LabelAutoFitStyle = LabelAutoFitStyles.None;
}
private delegate void DispLineChartDelegate(Chart chart, double value);
private void AddPoint(Chart chart, double value)
{
if (chart.InvokeRequired)
{
DispLineChartDelegate dispLineChartDelegate = new DispLineChartDelegate(AddPoint);
chart.Invoke(dispLineChartDelegate, new Object[] { chart, value });
}
else
{
Series series = chart.Series[0];
series.Points.AddXY(series.Points.Count, value);
ChartArea chartArea = chart.ChartAreas[0];
chartArea.AxisX.ScaleView.Position = series.Points.Count - 30;
chartArea.AxisX.ScaleView.Size = 30;
}
}
private void ClearRecord()
{
SqlCommand cmd = new SqlCommand();
cmd.CommandText = "truncate table SensorInfo";
cmd.Connection = sqlConnection;
cmd.ExecuteNonQuery();
}
private delegate void DispDatabaseDelegate();
private void DispDatabase()
{
if (dataGridView1.InvokeRequired)
{
DispDatabaseDelegate dispDatabaseDelegate = new DispDatabaseDelegate(DispDatabase);
dataGridView1.Invoke(dispDatabaseDelegate, null);
}
else
{
RrefreshDataView();
}
}
private void AddRecord(String device, String trwendu, String trshidu, String hwendu, String hshidu, String guangq, String dianliu, String shijian)
{
string sql = string.Format("INSERT INTO SensorInfo(device,tuwendu,tushidu,huanwendu,huanshidu,light,dianliu,time)VALUES('{0}','{1}','{2}','{3}','{4}','{5}','{6}','{7}')", device, trwendu, trshidu, hwendu, hshidu, guangq, dianliu, shijian);
SqlCommand cmd = new SqlCommand(sql, sqlConnection);
cmd.ExecuteNonQuery();
}
private void RrefreshDataView()
{
try
{
SqlDataAdapter adapter = new SqlDataAdapter("select device as 设备,tuwendu as 土壤温度,tushidu as 土壤湿度,huanwendu as 环境温度,huanshidu as 环境湿度,light as 光强,dianliu as 电流,time as 时间 from SensorInfo", sqlConnection);
DataSet set = new DataSet();
adapter.Fill(set, "SensorInfo");
dataGridView1.DataSource = set.Tables["SensorInfo"];
dataGridView1.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.DisplayedCells;
dataGridView1.FirstDisplayedScrollingRowIndex = dataGridView1.RowCount - 1;
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
private void dayinbaobiao_Click(object sender, EventArgs e)
{
ExportToExcel d = new ExportToExcel();
d.OutputAsExcelFile(dataGridView1);
}
private void btnDelete_Click(object sender, EventArgs e)
{
string id = comboBox1.SelectedItem.ToString().Trim();
string sql = String.Format("delete from SensorInfo where device='{0}'", id);
SqlCommand command = new SqlCommand(sql, sqlConnection);
command.ExecuteNonQuery();
RrefreshDataView();
}
private void button3_Click(object sender, EventArgs e)
{
richTextBox2.Clear();
}
}
}
有了代码照着布局就可以了,晚些时候我也会把工程上传到github上供大家参考。
最后
以上就是高贵荷花为你收集整理的物联网专业课程设计:温室监控系统——上位机篇(串口通信、SQL sever数据库、socket套接字)的全部内容,希望文章能够帮你解决物联网专业课程设计:温室监控系统——上位机篇(串口通信、SQL sever数据库、socket套接字)所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复