物联网工具——前后台分离(15)

259 阅读2分钟

之前章节的工具code放在gitee上,大家有兴趣可以下载。欢迎交流
gitee地址:gitee.com/vectorli/te…

ps:目前匿名访问刚申请,需要等些时间。大家7月5号以后可以再次尝试

架构分析

之前,我们制作的软件。采集服务和显示界面集中在一个软件中。
这样做的好处是,简单明了,安装方便
弊端就是,需要用户一直开启界面,才能保证数据不间断。先然,这个问题无法让人接受\

分离式架构

解决以上问题,我们需要采用分离式架构。
提供数据采集后端服务+数据显示和配置前端界面:

后端服务

  • 自动开机运行
  • 读取串口配置,并定时获取数据
  • 数据入库

前端界面

  • 实时数据展示
  • 历史数据查询
  • 报表导出
  • 报警提示

image.png

新建功能温湿度v2.0解决方案

  • 添加之前的界面项目
  • 新增一个win worker项目

image.png

worker 服务

把以下文件copy到work中去

  • 数据库操作类
  • 串口通讯类
  • 温湿度传感器类
  • 工具类

实现work的执行函数

work执行函数主要逻辑:

  • 打开串口
  • 启动定时器
  • 获取数据
  • main while
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.Data;
using System.IO.Ports;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using System.Timers;

namespace THWorker
{
    public class Worker : BackgroundService
    {
        /// <summary>
        /// 串口配置
        /// </summary>
        JObject ComConfInfo;
        /// <summary>
        /// 串口
        /// </summary>
        SerialPort Runport;
        /// <summary>
        /// 执行入口
        /// </summary>
        /// <param name="stoppingToken"></param>
        /// <returns></returns>
        protected override async Task ExecuteAsync(CancellationToken stoppingToken)
        {
            //获取串口配置
            if (!GetComInfo())
                return;
            //启动定时器
            StartMainTimer();
            //main while
            while (!stoppingToken.IsCancellationRequested)
            {
                MyLogger._.Info($"Worker running at: {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")}");
                await Task.Delay(30*1000, stoppingToken);
            }
        }

        #region 5s 定时器回调
        /// <summary>
        /// 获取串口配置
        /// </summary>
        private bool GetComInfo()
        {
            MyLogger._.Debug("获取串口配置");
            DataTable dt = DB_SerAPI.GetComConfFromDB();
            if (dt != null && dt.Rows.Count > 0)
            {
                ComConfInfo = JObject.Parse(dt.Rows[0]["data"].ToString());
                //打开串口
                string comname = ComConfInfo["port"].ToString();
                //波特率
                int baudrate = int.Parse(ComConfInfo["baute"].ToString());
                //打开串口
                Runport = ComPort.V_OpenPort8N1(comname, baudrate);
                if (Runport == null)
                {
                    MyLogger._.Error("打开串口失败");
                    return false;
                }
                return true;
            }
            else
            {
                MyLogger._.Error("获取串口配置失败,请重新配置");
                return false;
            }
        }
        /// <summary>
        /// Timer类执行定时到点事件
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void TimerOutCallBack(object sender, ElapsedEventArgs e)
        {
            MyLogger._.Debug("获取温湿度数据。。。");

            try
            {
                if (Runport.IsOpen)
                {
                    byte addr = 3;
                    if (ComConfInfo.ContainsKey("devaddr"))
                        addr = byte.Parse(ComConfInfo["devaddr"].ToString());
                    //获取数据
                    string ret = THSensor.ReadTHDataFromSensor(Runport, addr);
                    if (!string.IsNullOrEmpty(ret))
                    {
                        string temp = ret.Split('&')[0];
                        string humi = ret.Split('&')[1];
                        //入库操作
                        DB_SerAPI.SaveTHData(temp, humi);
                        //debug
                        MyLogger._.Debug($"T:{temp} - H:{humi}");
                    }
                    else
                        MyLogger._.Error("无数据,请检查配置参数");
                }
            }
            catch (Exception ex)
            {
                MyLogger._.Error(ex.Message + "\r\n" + ex.StackTrace);
            }
        }

        /// <summary>
        /// 启动timer
        /// </summary>
        public void StartMainTimer()
        {
            System.Timers.Timer Read_Timer;
            //设置定时间隔(毫秒为单位) 5s
            int interval = 5*1000;
            Read_Timer = new System.Timers.Timer(interval);
            Read_Timer.AutoReset = true;
            Read_Timer.Enabled = true;
            Read_Timer.Elapsed += new ElapsedEventHandler((s, e) => TimerOutCallBack(s, e));
            Read_Timer.Start();
        }
        #endregion
    }
}

编译运行

image.png

编写服务安装和卸载bat脚本

关于win服务的安装和卸载脚本网上帖子很多,有兴趣可以查询。

卸载服务

echo off
::停止服务
sc stop TH_DATA_SER
::删除服务
sc delete TH_DATA_SER
::回车退出
set /p temp="按下回车键退出"

安装服务

echo off
::输出当前路径
echo 当前路径:%~dp0
::安装服务
echo %~dp0\THWorker.exe
::服务路径
set binPath= %~dp0%\THWorker.exe
::echo %binPath
::安装服务
sc create TH_DATA_SER binPath= %binPath%
::自动启动
sc config TH_DATA_SER start= auto
::启动服务
net start TH_DATA_SER
::回车退出
set /p temp="按下回车键退出"

测试服务安装

image.png

查看系统服务

image.png

下一章节介绍修改本地操作界面