数字农业管理系统是提供给农业大棚管理员的,通过传感器获取到的大棚环境状态可视化展示,从而对大棚作物的生长状态进行监控和管理的平台。
此系统主要分为两大部分,第一部分是获取大棚中与作物生长环境有关的一系列数据,以及对相应数据进行管理和可视化的监控平台,第二部分是给不同管理员之间提供的相互留言的留言板,以及高级管理者对所有管理员之间留言的查看与删除。
使用到的知识
1.B\S架构
BS即Browser/Server(浏览器/服务器)结构,就是只安装维护一个服务器(Server),而客户端选用浏览器(Browse)运行软件。B/S结构应用程序相对于传统的C/S结构应用程序就是一个特别大的进步。 B/S结构的重要特征就是分布性强、维护方便、开发简单并且共享性强、总体拥有费用低。但数据安全性问题、对服务器需要过高、数据传输速度慢、软件的个性化特征明显减少,这些缺点就是有目共睹的,难以完成传统形式下的特殊功能请求。比如通过浏览器实行大量的数据输入或实行报表的应答、专用性打印输出全部相对比较困难与不便。另外,完成复杂的应用构造有较大的困难。
2.Tomcat
Tomcat 服务器是一个免费的开放源代码的Web 应用服务器,属于轻量级应用服务器,在中小型系统和并发访问用户不是很多的场合下被普遍使用,是开发和调试JSP 程序的首选。对于一个初学者来说,可以这样认为,当在一台机器上配置好Apache 服务器,可利用它响应HTML(标准通用标记语言下的一个应用)页面的访问请求。实际上Tomcat是Apache 服务器的扩展,但运行时它是独立运行的,所以当你运行tomcat 时,它实际上作为一个与Apache 独立的进程单独运行的。
3.Echart
ECharts,一个使用 JavaScript 实现的开源可视化库,可以流畅的运行在 PC 和移动设备上,兼容当前绝大部分浏览器(IE8/9/10/11,Chrome,Firefox,Safari等),底层依赖轻量级的矢量图形库 ZRender ,提供直观,交互丰富,可高度个性化定制的数据可视化图表。
4.qt
Qt使用标准的C++和特殊的代码生成扩展(称为元对象编译器(Meta Object Compiler, moc))以及一些宏。通过语言绑定,其他的编程语言也可以使用Qt。Qt 是一个1991年由Qt Company开发的跨平台C++图形用户界面应用程序开发框架。它既可以开发GUI程序,也可用于开发非GUI程序,比如控制台工具和服务器。Qt是面向对象的框架,使用特殊的代码生成扩展(称为元对象编译器(Meta Object Compiler, moc))以及一些宏,Qt很容易扩展,并且允许真正地组件编程。优良的跨平台特性:、面向对象、丰富的 API。
1.登录
2.注册
3.进入功能界面
4.可视化功能
1.arduino及串口
实验目的
掌握传感器的使用方法,掌握Arduino的使用方法,掌握数据库的开发方法,提升产品设计与架构能力,掌握Arduino的串口等通信方法,提升面向对象编程能力.
实验器材:
Arduino UNO
DHT11 温湿度传感器
光敏电阻传感器
土壤湿度传感器
继电器*2
Win10主机,SQLserver数据库,Python3等开发软件
实验原理:
简述:读取数据->传输数据->自响应
包括基于Qt开发的本地显示软件以及Web显示功能
总体架构设计
Arduino+SQLServer+Web+Qt
#include <dht11.h>
#define DHT11_Output 5
#define Light_Output 4
#define Relay_INPUT1 6
#define Relay_INPUT2 7
#define MS_INPUTA PIN_A0
#define MS_INPUTD 3
class MyDigitalOutput{
public:
int Dpin;
int state;
MyDigitalOutput(int Dpin,int state=0):Dpin(Dpin){
this->state = state;
pinMode(Dpin,OUTPUT);
digitalWrite(Dpin,this->state);
}
~MyDigitalOutput(){}
void Open(){
this->state = 1;
digitalWrite(Dpin,this->state);
}
void Close(){
this->state = 0;
digitalWrite(Dpin,this->state);
}
};
class MyDigitalInput{
public:
int Dpin;
MyDigitalInput(int Dpin):Dpin(Dpin){
pinMode(Dpin,INPUT);
}
~MyDigitalInput(){}
bool DGet(){ //Bool
return digitalRead(this->Dpin);
}
};
class MyAnalogInput{
public:
int Apin;
MyAnalogInput(int Apin):Apin(Apin){
pinMode(Apin,INPUT);
}
~MyAnalogInput(){}
double AGet(){
return analogRead(Apin);
}
};
//土壤湿度
class MyMoistureSensor:public MyAnalogInput,public MyDigitalInput{
public:
MyMoistureSensor(int Dpin,int Apin):MyDigitalInput(Dpin),MyAnalogInput(Apin){}
~MyMoistureSensor(){}
double Moisture(){
return 1-(AGet()-245)/(1023-245);
}
};
//光照
class MyLightSensor:public MyDigitalInput{
public:
MyLightSensor(int pin):MyDigitalInput(pin){}
~MyLightSensor(){}
};
//温湿度方法
double Fahrenheit(double celsius){ //华氏度
return 1.8 * celsius + 32;
}
class MyDHT11{
public:
dht11 equip;
int pin;
MyDHT11(int pin):pin(pin){
pinMode(pin,INPUT);
}
~MyDHT11(){}
int UpDate(){
int state = equip.read(this->pin);
return state;
}
double Temperature(){
return (float)this->equip.temperature;
}
double Humidity(){
return (float)this->equip.humidity;
}
};
//MyRelay
class MyRelay:public MyDigitalOutput{
public:
MyRelay(int pin,int state=0):MyDigitalOutput(pin,state){}
~MyRelay(){};
};
void setup() {
Serial.begin(9600);
//pinMode(DHT11_Output,INPUT);
//pinMode(Light_Output,INPUT);
}
void stdPrint(String key,double temp){
Serial.print(key);
Serial.print(":");
Serial.print(temp);
Serial.print(",");
}
void stdPrint(String key,String temp){
Serial.print(key);
Serial.print(":");
Serial.print(temp);
Serial.print(",");
}
void stdPrint(String key,bool temp){
Serial.print(key);
Serial.print(":");
Serial.print(temp==false?0:1);
Serial.print(",");
}
MyDHT11 dht(DHT11_Output);
MyLightSensor lightSensor(Light_Output);
MyRelay relay1(Relay_INPUT1);
MyRelay relay2(Relay_INPUT2);
MyMoistureSensor ms(MS_INPUTD,MS_INPUTA);
void loop() {
// put your main code here, to run repeatedly:
dht.UpDate();
stdPrint("Temperature",dht.Temperature());
stdPrint("Humidity",dht.Humidity());
stdPrint("Light",lightSensor.DGet());
stdPrint("Moisture",ms.Moisture());
Serial.println("");
/*
Serial.println(dht.Temperature());
Serial.println(dht.Humidity());
Serial.println(lightSensor.DGet());
Serial.println(ms.Moisture());
Serial.println(ms.DGet());
Serial.println(js);
*/
if(ms.Moisture()<0.2){
relay1.Open();
}else{
relay1.Close();
}
if(lightSensor.DGet()==true){
relay2.Open();
}else{
relay2.Close();
}
delay(2000);
}
#include <dht11.h>
#define DHT11_Output 5
#define Light_Output 4
#define Relay_INPUT1 6
#define Relay_INPUT2 7
#define MS_INPUTA PIN_A0
#define MS_INPUTD 3
class MyDigitalOutput{
public:
int Dpin;
int state;
MyDigitalOutput(int Dpin,int state=0):Dpin(Dpin){
this->state = state;
pinMode(Dpin,OUTPUT);
digitalWrite(Dpin,this->state);
}
~MyDigitalOutput(){}
void Open(){
this->state = 1;
digitalWrite(Dpin,this->state);
}
void Close(){
this->state = 0;
digitalWrite(Dpin,this->state);
}
};
class MyDigitalInput{
public:
int Dpin;
MyDigitalInput(int Dpin):Dpin(Dpin){
pinMode(Dpin,INPUT);
}
~MyDigitalInput(){}
bool DGet(){ //Bool
return digitalRead(this->Dpin);
}
};
class MyAnalogInput{
public:
int Apin;
MyAnalogInput(int Apin):Apin(Apin){
pinMode(Apin,INPUT);
}
~MyAnalogInput(){}
double AGet(){
return analogRead(Apin);
}
};
//土壤湿度
class MyMoistureSensor:public MyAnalogInput,public MyDigitalInput{
public:
MyMoistureSensor(int Dpin,int Apin):MyDigitalInput(Dpin),MyAnalogInput(Apin){}
~MyMoistureSensor(){}
double Moisture(){
return 1-(AGet()-245)/(1023-245);
}
};
//光照
class MyLightSensor:public MyDigitalInput{
public:
MyLightSensor(int pin):MyDigitalInput(pin){}
~MyLightSensor(){}
};
//温湿度方法
double Fahrenheit(double celsius){ //华氏度
return 1.8 * celsius + 32;
}
class MyDHT11{
public:
dht11 equip;
int pin;
MyDHT11(int pin):pin(pin){
pinMode(pin,INPUT);
}
~MyDHT11(){}
int UpDate(){
int state = equip.read(this->pin);
return state;
}
double Temperature(){
return (float)this->equip.temperature;
}
double Humidity(){
return (float)this->equip.humidity;
}
};
//MyRelay
class MyRelay:public MyDigitalOutput{
public:
MyRelay(int pin,int state=0):MyDigitalOutput(pin,state){}
~MyRelay(){};
};
void setup() {
Serial.begin(9600);
//pinMode(DHT11_Output,INPUT);
//pinMode(Light_Output,INPUT);
}
void stdPrint(String key,double temp){
Serial.print(key);
Serial.print(":");
Serial.print(temp);
Serial.print(",");
}
void stdPrint(String key,String temp){
Serial.print(key);
Serial.print(":");
Serial.print(temp);
Serial.print(",");
}
void stdPrint(String key,bool temp){
Serial.print(key);
Serial.print(":");
Serial.print(temp==false?0:1);
Serial.print(",");
}
MyDHT11 dht(DHT11_Output);
MyLightSensor lightSensor(Light_Output);
MyRelay relay1(Relay_INPUT1);
MyRelay relay2(Relay_INPUT2);
MyMoistureSensor ms(MS_INPUTD,MS_INPUTA);
void loop() {
// put your main code here, to run repeatedly:
dht.UpDate();
stdPrint("Temperature",dht.Temperature());
stdPrint("Humidity",dht.Humidity());
stdPrint("Light",lightSensor.DGet());
stdPrint("Moisture",ms.Moisture());
Serial.println("");
/*
Serial.println(dht.Temperature());
Serial.println(dht.Humidity());
Serial.println(lightSensor.DGet());
Serial.println(ms.Moisture());
Serial.println(ms.DGet());
Serial.println(js);
*/
if(ms.Moisture()<0.2){
relay1.Open();
}else{
relay1.Close();
}//打开继电器一浇水
if(lightSensor.DGet()==true){
relay2.Open();
}else{
relay2.Close();
}//打开继电器二开灯
delay(2000);
}
ser.py 用于读串口数据并放入数据库中pymssql,pymssql是python用来连接Microsoft SQL Server的一个工具库 (package)
pymssql的使用十分简单,基本就如下几个步骤
- 创建链接:使用connect()创建连接并获取Connection对象
- 交互操作:获取Connection对象的Cursor对象,然后使用Cursor对象的各种方法与数据库进行交互
- 关闭链接
import serial # 导入串口包
import time # 导入时间包
ser = serial.Serial("COM8",9600,timeout = 5)
ser.flushInput() # 清空缓冲区
import pymssql,sys,os,time,datetime,random
SSMSCONFIG = {
"host":'127.0.0.1',
"user":'sa',
"password":'111',
"database":"DigitalAgriculture",
"charset":'GBK'
}
class SQLServerCore(object):
def __init__(self,host='127.0.0.1',user='sa',password='111',database='test',charset='utf8',workPath='./'):
self.connection = pymssql.connect( host=host,
user=user,
password=password,
database=database,
charset=charset)
self.BETA = True
self.cursor = self.connection.cursor()
self.stderr = sys.stderr
self.errFile = open(workPath+'errFile.txt','a')
def insertMany(self,table,args:list,datas):
args = ','.join(args)
cmd = "INSERT INTO {} VALUES ({})".format(table,args)
self.BetaPrintCmd(cmd)
try:
self.cursor.executemany(cmd,datas)
self.connection.commit()
except BaseException as e:
self.BetaPrintError(e)
'''
args:
(%s,%s,%d,%f)
datas:
[(1, 'John Smith', 'John Doe'),
(2, 'Jane Doe', 'Joe Dog'),
(3, 'Mike T.', 'Sarah H.')]
'''
def execute(self,cmd):
try:
self.BetaPrintCmd(cmd)
except BaseException as e:
self.BetaPrintError(e)
self.cursor.execute(cmd)
def get(self,table,args:list):
args = ','.join(args)
cmd = 'select {} from {}'.format(args,table)
self.BetaPrintCmd(cmd)
self.cursor.execute(cmd)
return self.cursor.fetchall()
def getAll(self,table):
self.cursor.execute('select * from {}'.format(table))
return self.cursor.fetchall()
def __del__(self):
self.connection.close()
sys.stderr = self.stderr
self.errFile.close()
def BetaPrintCmd(self,cmd):
if self.BETA:
print(">>> 执行了命令: {}".format(cmd))
def BetaPrintError(self,e:BaseException):
s = ">>> 发生了错误: {}".format(e)
if self.BETA:
print(s)
self.BetaErrWrite(s)
def BetaErrWrite(self,err):
sys.stderr = self.errFile
sys.stderr.write(err+'\n')
sys.stderr = self.stderr
if __name__=='__main__':
myServer = SQLServerCore(**SSMSCONFIG)
while True:
count = ser.inWaiting() # 获取串口缓冲区数据
if count !=0 :
time.sleep(0.2) # 延时0.1秒,免得CPU出问题
recv = ser.read(ser.in_waiting).decode("utf-8") # 读出串口数据,数据采用gbk编码
print(time.time()," --- recv --> ", recv) # 打印一下子
temp = []
datas = recv.lstrip().rstrip().split(',')
for data in datas:
#print(data)
try:
temp.append(float(data.split(':')[1]))
except:
pass
print(temp)
print('输入一条数据')
timeStamp = time.time()
dateArray = datetime.datetime.fromtimestamp(timeStamp)
timer = dateArray.strftime("%Y-%m-%d %H:%M:%S")
try:
data = ("'UNO-0000'","'Arduino'",str(int(timeStamp)),"'{}'".format(timer),str(temp[0]),str(temp[1]),str(temp[3]))
print('insert one message!\n')
except Exception as e:
print(e)
myServer.execute('INSERT INTO dbo.STDRecord1 VALUES({});'.format(','.join(data)))
myServer.connection.commit()
解析串口传过来的数据
import pymssql,sys,os,time,datetime,random,serial
SSMSCONFIG = {
"host":'127.0.0.1',
"user":'sa',
"password":'111',
"database":"DigitalAgriculture",
"charset":'utf8'
}
class SQLServerCore(object):
def __init__(self,host='127.0.0.1',user='sa',password='111',database='test',charset='utf8',workPath='./'):
self.connection = pymssql.connect( host=host,
user=user,
password=password,
database=database,
charset=charset)
self.BETA = True
self.cursor = self.connection.cursor()
self.stderr = sys.stderr
self.errFile = open(workPath+'errFile.txt','a')
def insertMany(self,table,args:list,datas):
args = ','.join(args)
cmd = "INSERT INTO {} VALUES ({})".format(table,args)
self.BetaPrintCmd(cmd)
try:
self.cursor.executemany(cmd,datas)
self.connection.commit()
except BaseException as e:
self.BetaPrintError(e)
'''
args:
(%s,%s,%d,%f)
datas:
[(1, 'John Smith', 'John Doe'),
(2, 'Jane Doe', 'Joe Dog'),
(3, 'Mike T.', 'Sarah H.')]
'''
def execute(self,cmd):
try:
self.BetaPrintCmd(cmd)
except BaseException as e:
self.BetaPrintError(e)
self.cursor.execute(cmd)
def get(self,table,args:list):
args = ','.join(args)
cmd = 'select {} from {}'.format(args,table)
self.BetaPrintCmd(cmd)
self.cursor.execute(cmd)
return self.cursor.fetchall()
def getAll(self,table):
self.cursor.execute('select * from {}'.format(table))
return self.cursor.fetchall()
def __del__(self):
self.connection.close()
sys.stderr = self.stderr
self.errFile.close()
def BetaPrintCmd(self,cmd):
if self.BETA:
print(">>> 执行了命令: {}".format(cmd))
def BetaPrintError(self,e:BaseException):
s = ">>> 发生了错误: {}".format(e)
if self.BETA:
print(s)
self.BetaErrWrite(s)
def BetaErrWrite(self,err):
sys.stderr = self.errFile
sys.stderr.write(err+'\n')
sys.stderr = self.stderr
myServer = SQLServerCore(**SSMSCONFIG)
ser = serial.Serial("COM8",9600,5)
ser.flushInput()
ser.flushOutput()
while True:
print('输入一条数据')
recv = ""
while True:
if ser.inWaiting()>0:
recv += ser.read().decode('utf-8')
elif recv.endswith('\n'):
break
#recv = recv.lstrip(',').split(',')
print(">>>接收到的数据:",recv)
timeStamp = time.time()
dateArray = datetime.datetime.fromtimestamp(timeStamp)
timer = dateArray.strftime("%Y-%m-%d %H:%M:%S")
data = ("'UNO-0000'","'Arduino'",str(int(timeStamp)),"'{}'".format(timer),recv[1],recv[3],recv[7])
myServer.execute('INSERT INTO dbo.STDRecord1 VALUES({});'.format(','.join(data)))
myServer.connection.commit()
time.sleep(1)
2.echartCore.py
from pyecharts import options as opts
from pyecharts.charts import Bar, Grid, Line, Liquid, Page, Pie
from pyecharts.commons.utils import JsCode
from pyecharts.components import Table
from pyecharts.faker import Faker
def bar_base(v:list,name:str) ->Bar:
bar=(
Bar()
.add_xaxis([int(len(v)-x) for x in range(len(v))])
.add_yaxis(name,v)
.set_global_opts(
title_opts=opts.TitleOpts(title=name),
datazoom_opts=[opts.DataZoomOpts(), opts.DataZoomOpts(type_="inside")]
)
)
return bar
def table_base(headers=[],rows=[],title:str="default") -> Table:
table = Table()
table.add(headers, rows).set_global_opts(
title_opts=opts.ComponentTitleOpts(title=title)
)
return table
def water(val1,title:str) ->Liquid:
c=(Liquid()
.add('water',[val1,val1+0.05],center=['20%','30%'])
.set_global_opts(title_opts=opts.TitleOpts(title=title))
)
return c
def page_simple_layout(widgets:list,saveFile="./data_visiable.html"):
page = Page(layout=Page.SimplePageLayout)
page.add(
*widgets
)
page.render(saveFile)
visiable1.py
#本脚本针对特定EId设备进行可视化
import EchartCore
import SQLServerCore
myServer = SQLServerCore.SQLServerCore(**SQLServerCore.SSMSCONFIG)
myServer.execute("SELECT TOP(10) EId,R1Temperature,R1Humidity,R1SoilMoisture\
FROM UserTable1\
WHERE EId = 'VE1-0001'\
ORDER BY R1FlagTime DESC\
")
data = myServer.cursor.fetchall()
#print(data)
temperature = EchartCore.bar_base([data[i][1] for i in range(len(data))],'Temperature')
humidity = EchartCore.bar_base([data[i][2] for i in range(len(data))],'Humidity')
soilMoisture = EchartCore.bar_base([data[i][3] for i in range(len(data))],'SoilMoisture')
nowHumidity = EchartCore.water(data[0][2],'NowHumidity')
nowSoilMoisture = EchartCore.water(data[0][3],'NowSoilMoisture')
EchartCore.page_simple_layout([nowHumidity,nowSoilMoisture,temperature,humidity,soilMoisture],"../visiable1.html")
visiable2.py
#本脚本针对天气进行可视化
import EchartCore
import SQLServerCore
myServer = SQLServerCore.SQLServerCore(**SQLServerCore.SSMSCONFIG)
myServer.execute("\
SELECT * FROM Weather\
ORDER BY WFlagTime DESC\
")
data = myServer.cursor.fetchall()
table = EchartCore.table_base(['请求时间戳','数据更新时间','地区','天气','温度'],data,"天气情况")
EchartCore.page_simple_layout([table],"../visiable2.html")
5.2设计体会
经过这次数字农业管理系统设计。在这次课程设计过程中,我深刻认识到学习一门课程并不只是单纯的仅仅学好这门课程。现实需求的实现往往极其复杂,所涉及到的技术单纯一门学科无论学的再好也远远不能满足,真正需要的能力是把多学科的融会贯通,将多学科结合起来使用,比如在本次实验中,涉及到了传感器的应用,ARDUNIO开发板的编程,串口通信技术,数据库设计,web网页开发等一系列多学科技术。
“众人拾柴火焰高”,在这短短的时间里,要想实现对我来说这样一个复杂的综合性系统,仅仅靠自己的力量是远远不够的。团队的力量是可畏的,通过我们小组成员的分工合作,我深刻感受到团队合作的力量,将整个系统层层分工下来,每个人负责一部分功能的实现,大家还可以相互交流,相互帮助,大大提升了开发的进度和速度。因此要善于团队合作,善于利用别人的智慧,这才是真正的智慧。
“实践出真知”,理论课学的再好,如果不能将它付诸实践,一切都是空把式。我深刻意识到自己动手能力的欠缺,在时间工程中,常常脑子中有相关的理论知识,却不知道如何在项目中使用。只有多多动手,将课本中的知识应用到实践中去,才能更加深刻的理解,才能转换成真正有用的知识,只会纸上谈兵,是无法体现一个人的价值与能力的。
“不能则学,不知则问”,要想实现一个完整的项目,课本中的知识是远远不够的,比如我们的json解析天气数据,比如我们课本中未提及的CSS具体开发,都需要我们上网查询资料。面对自己能力的不足,一定要勤学好问,不懂就查,不懂就问,学无止境,不要以为自己学会课本中的知识就学得够多了,实际上,这是远远不足的,远远不够的。只有勤学好问,才能不断进步,才能真正提升自己的能力。