ps:旧技术了,有一点局限性,但好像网上记录并不多,或是很零散,比较坑人。故自己记录一下。
项目环境:
win 10、.Net framework 4.8,Visual Studio 2019,oracle 12G,ORM是SqlSugar5.1.4.96
一、建项目
1、建项目
新增解决方案文件夹,添加,新建项目,选择windows服务(.net framework),以我个人项目为例,名称为WinService.ReadLog,如下图
2、加入引用
2.1 把实体类库和业务层类库加入。如图
2.2 加入引用Oracle.ManagedDataAccess版本4.122.1.0,加入引用System.Transactions版本4.0.0.0,NuGet管理SqlSugar版本5.1.4.96
二、编码
1、App.config
填上必要参数,包括自定义参数,oracle连接,oracle配置等。
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<connectionStrings>
<add name="OracleConn" connectionString="User Id=sajet;Password=tech;Data Source=10.0.1.152/MNCMES;" providerName="Oracle.ManagedDataAccess.Client" />
</connectionStrings>
<appSettings>
<add key="PLINE" value="A" />
<!--测试机-->
<add key="MACHING_CODE" value="8C1759DD11DE" />
<!--同步日志地址-->
<add key="SYNC_LOGPATH" value="D:\电源自动测试系统\数据库" />
</appSettings>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.8" />
</startup>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.Memory" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.0.1.2" newVersion="4.0.1.2" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Buffers" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.0.3.0" newVersion="4.0.3.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Numerics.Vectors" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.1.4.0" newVersion="4.1.4.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Runtime.CompilerServices.Unsafe" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-5.0.0.0" newVersion="5.0.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Oracle.ManagedDataAccess" publicKeyToken="89b483f429c47342" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.122.1.0" newVersion="4.122.1.0" />
</dependentAssembly>
</assemblyBinding>
</runtime>
<system.data>
<DbProviderFactories>
<remove invariant="Oracle.ManagedDataAccess.Client" />
<add name="ODP.NET, Managed Driver" invariant="Oracle.ManagedDataAccess.Client" description="Oracle Data Provider for .NET, Managed Driver" type="Oracle.ManagedDataAccess.Client.OracleClientFactory, Oracle.ManagedDataAccess, Version=4.122.21.1, Culture=neutral, PublicKeyToken=89b483f429c47342" />
</DbProviderFactories>
</system.data>
</configuration>
2、Service1
选中Service1,按F7,进入。参考代码如下,包括计时器的使用,简单的日志记录,服务开启和关闭的正常代码等。
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.ServiceProcess;
using System.Text;
using System.Threading.Tasks;
using System.Timers;
using System.Configuration;
using DAL;
using Model;
using System.Transactions;
namespace WinService.ReadLog
{
public partial class Service1 : ServiceBase
{
private Timer timer;
string serviceName = "测试机日志读取";
public Service1()
{
InitializeComponent();
timer = new Timer(5000); // 设置定时器间隔为60秒
timer.Elapsed += Timer_Elapsed;
timer.AutoReset = true;
}
#region 服务开启
protected override void OnStart(string[] args)
{
WriteInfo($"[{serviceName}]服务开启");
timer.Start();
base.OnStart(args);
}
#endregion
#region 服务关闭
protected override void OnStop()
{
WriteInfo($"[{serviceName}]服务关闭");
timer.Stop();
base.OnStop();
}
#endregion
#region 轮询事件
private void Timer_Elapsed(object sender, ElapsedEventArgs e)
{
try
{
在这里编写你的轮询逻辑
//string logMessage = $"轮询任务执行于 {DateTime.Now.ToString()}";
//WriteLog(logMessage);
#region 1.检查配置参数
//1.检查配置参数(良品率阈值、产线、测试机、同步日志地址)
WriteInfo($"程序启动,检查参数...");
string Pline = UnitHelper.GetPline();//产线
if (string.IsNullOrEmpty(Pline))
{
throw new Exception("错误!!缺少参数,“产线”");
}
string TestMachingCode = UnitHelper.GetTestMachingCode();//测试机
string mac = UnitHelper.GetMac();
if (string.IsNullOrEmpty(TestMachingCode))
{
throw new Exception($"错误!!缺少参数,“测试机”。系统检测,物理地址为{mac}");
}
if (TestMachingCode != mac)
{
throw new Exception("错误!!物理地址不匹配!");
}
string LogPath = UnitHelper.GetLogPath();//同步日志地址
if (string.IsNullOrEmpty(LogPath))
{
throw new Exception("错误!!缺少参数,“同步日志地址”");
}
if (!Directory.Exists(LogPath))
{
throw new Exception("错误!!不存在的路径,“同步日志地址”");
}
var allLineList = DbHelper.Db.Queryable<SYNC_LINE>().ToList();
if (allLineList.Count < 10)
{
throw new Exception("错误!!缺失系统数据(良品率阈值)");
}
#endregion
//2、查看当天日志,读取待更新的数据
WriteInfo("开始同步......请勿中途关闭程序!");
//查看当天数据是否存在
DateTime today = DateTime.Today;
string todaFolder = LogPath + "\\" + UnitHelper.DateToString(today);
if (!Directory.Exists(todaFolder))
{
throw new Exception("今日数据未更新!");
}
//获取当前目录的所有excel文件
string[] excelFiles = Directory.GetFiles(todaFolder, "*.xls*", SearchOption.AllDirectories);
int succ = 0;//成功数
//循环excel
for (int z = 0; z < excelFiles.Count(); z++)
{
//文件完整路径
string fullName = excelFiles[z];
using (TransactionScope ts = new TransactionScope(TransactionScopeOption.Required, new TransactionOptions { Timeout = TimeSpan.FromMinutes(10) }))
{
//起始行
var fileModel = DbHelper.Db.Queryable<SYNC_READDATA>().Where(a => a.PLINE == Pline &&
a.MACHING_CODE == TestMachingCode &&
a.FULL_NAME == fullName).First();
int startNum = 0;
if (fileModel != null)
{
startNum = fileModel.ROW_NO;
}
//最终行
int lastNum = UnitHelper.sheetRowNum(fullName);
if (startNum >= lastNum)
{
ts.Complete();//提交
throw new Exception("没有更新数据!");
}
SYNC_READDATA main = new SYNC_READDATA();
if (fileModel == null)
{
main.PLINE = Pline;
main.MACHING_CODE = TestMachingCode;
main.SYNC_LOGPATH = LogPath;
main.SYNC_LOGDATE = today;
main.FULL_NAME = fullName;
main.ROW_NO = lastNum;
main.UPDATE_DATE = DateTime.Now;
//4.1同步表SYNC_READDATA
DbHelper.Db.Insertable(main).ExecuteCommand();
}
else
{
main = fileModel;
main.ROW_NO = lastNum;
main.UPDATE_DATE = DateTime.Now;
//4.1同步表SYNC_READDATA
DbHelper.Db.Updateable(main).ExecuteCommand();
}
main.ID = DbHelper.Db.Ado.GetInt($"select ID from SYNC_READDATA where PLINE='{Pline}' and MACHING_CODE='{TestMachingCode}' and FULL_NAME='{fullName}'");
WriteInfo($"读取文件:\r\n {fullName}");
List<SYNC_READDATA_REC> recList = new List<SYNC_READDATA_REC>();
//读取待更新的数据
UnitHelper.GetRecList(main.ID, fullName, TestMachingCode, recList, startNum, lastNum);
//4.2同步记录表SYNC_READDATA_REC
DbHelper.Db.Insertable(recList).ExecuteCommandAsync();
SYNC_RES res = new SYNC_RES();
res.MID = main.ID;
res.PLINE = Pline;
res.CREATEDATE = DateTime.Now;
res.ADDNUM = recList.Count;
res.BADNUM = recList.Where(a => a.TEST_RES == "FALL").Count();
//4.3同步结果表SYNC_RES
DbHelper.Db.Insertable(res).ExecuteCommand();
var first4 = DbHelper.Db.Queryable<SYNC_CUR_STATISTICS>().Where(a => a.TODAY == today && a.PLINE == Pline).First();
if (first4 == null)
{
SYNC_CUR_STATISTICS curStatistics = new SYNC_CUR_STATISTICS();
curStatistics.TODAY = today;
curStatistics.PLINE = Pline;
curStatistics.TOTAL = res.ADDNUM;
curStatistics.BADNUM = res.BADNUM;
curStatistics.GP = Convert.ToDecimal((curStatistics.TOTAL - curStatistics.BADNUM)) / curStatistics.TOTAL;
curStatistics.GOAL = allLineList.Where(a => a.PLINE == Pline).First().GOAL;
curStatistics.ALERT = curStatistics.GP < curStatistics.GOAL ? 1 : 0;//是否报警
curStatistics.LIFT = 0;
curStatistics.ALERT_FLAG = curStatistics.ALERT;//报警状态
//4.4同步结果表SYNC_CUR_STATISTICS
DbHelper.Db.Insertable(curStatistics).ExecuteCommand();
}
else
{
first4.TOTAL = first4.TOTAL + res.ADDNUM;
first4.BADNUM = first4.BADNUM + res.BADNUM;
first4.GP = Convert.ToDecimal((first4.TOTAL - first4.BADNUM)) / first4.TOTAL;
first4.ALERT = first4.GP < first4.GOAL ? 1 : 0;
if (first4.LIFT == 0 && first4.ALERT == 1)
{
first4.ALERT_FLAG = 1;//报警状态
}
//4.4同步结果表SYNC_CUR_STATISTICS
DbHelper.Db.Updateable(first4).WhereColumns(it => new { it.TODAY, it.PLINE }).ExecuteCommand();
}
ts.Complete();//提交
}
succ++;
WriteInfo("成功导入:\r\n " + fullName);
}
if (succ == excelFiles.Count())
{
WriteInfo("全部同步完成");
WriteInfo("--------------------------------------------------------");
}
}
catch (Exception ex)
{
WriteInfo(ex.Message);
}
finally
{
WriteInfo("程序结束");
}
}
#endregion
#region 日志
//string logPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "MyPollingService.log");
private void WriteInfo(string message)
{
string catalog = @"d:/WinServiceLog/mpreadlog/";
//判断有无目录,没有自动创建
if (!Directory.Exists(catalog))
{
Directory.CreateDirectory(catalog);
}
string logPath = catalog + DateTime.Now.ToString("yyyy-MM-dd")+".log";
File.AppendAllText(logPath, $"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")} - {message}{Environment.NewLine}");
}
#endregion
}
}
3、添加安装程序
双击Service1.cs,添加安装程序,得到ProjectInstaller.cs
3.1 选择本地系统账号,如图。
3.2
Description:服务描述
DisplayName:服务名称
ServiceName:服务配置
StartType:服务启动类型,选择Automatic为开机自动启动
三、生成项目
右键重新生成项目,打开根目录,在\bin\Debug,下会生成相应程序。这时候需要手写一个安装文件和卸载文件,安装或是卸载时都需要用管理员模式运行。
1、安装文件Install.bat
%SystemRoot%\Microsoft.NET\Framework\v4.0.30319\installutil.exe %~dp0\WinService.ReadLog.exe
Net Start mp.readlog
sc config mpreadlog start= auto
pause
2、卸载文件UnInstall.bat
Net stop mp.readlog
%SystemRoot%\Microsoft.NET\Framework\v4.0.30319\installutil.exe /u %~dp0\WinService.ReadLog.exe
pause