diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..695d2b0 --- /dev/null +++ b/.gitignore @@ -0,0 +1,116 @@ +# ---> C Sharp +# Build Folders (you can keep bin if you'd like, to store dlls and pdbs) +[Bb]in/ +[Oo]bj/ + +# mstest test results +TestResults + +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. + +# User-specific files +*.suo +*.user +*.sln.docstates + +# Build results +[Dd]ebug/ +[Rr]elease/ +x64/ +*_i.c +*_p.c +*.ilk +*.meta +*.obj +*.pch +*.pdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.log +*.vspscc +*.vssscc +*.cache +*.dll +*.pdb +.builds + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opensdf +*.sdf + +# Visual Studio profiler +*.psess +*.vsp +*.vspx + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper* + +# NCrunch +*.ncrunch* +.*crunch*.local.xml + +# Installshield output folder +[Ee]xpress + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish + +# Publish Web Output +*.Publish.xml + +# NuGet Packages Directory +packages + +# Windows Azure Build Output +csx +*.build.csdef + +# Windows Store app package directory +AppPackages/ + +# Others +[Bb]in +[Oo]bj +sql +TestResults +[Tt]est[Rr]esult* +*.Cache +ClientBin +[Ss]tyle[Cc]op.* +~$* +*.dbmdl +Generated_Code #added for RIA/Silverlight projects + +# Backup & report files from converting an old project file to a newer +# Visual Studio version. Backup files are not needed, because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML + +/lib +.vs +/.gitattributes diff --git a/POSCorePrint.sln b/POSCorePrint.sln new file mode 100644 index 0000000..5b291ed --- /dev/null +++ b/POSCorePrint.sln @@ -0,0 +1,25 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.29123.88 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "POSCorePrint", "POSCorePrint/POSCorePrint.csproj", "{C340E6AE-33B9-4C36-A84A-4B2E924C82AC}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {C340E6AE-33B9-4C36-A84A-4B2E924C82AC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C340E6AE-33B9-4C36-A84A-4B2E924C82AC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C340E6AE-33B9-4C36-A84A-4B2E924C82AC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C340E6AE-33B9-4C36-A84A-4B2E924C82AC}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {444EB795-1EE2-4EDA-8747-1FF859AA80FF} + EndGlobalSection +EndGlobal diff --git a/POSCorePrint/Constant.cs b/POSCorePrint/Constant.cs new file mode 100644 index 0000000..d6ec4e7 --- /dev/null +++ b/POSCorePrint/Constant.cs @@ -0,0 +1,19 @@ +using Microsoft.Extensions.Configuration; +using MySql.Data.MySqlClient; +using POSCorePrint.Jobs; +using System; +using System.Collections.Generic; +using System.Data; +using System.Text; + +namespace POSCorePrint +{ + internal class Constant + { + internal static int port = 9100; + internal static IConfigurationRoot Configuration; + internal static string ConnectionString; + internal static double PrintBackTime;//打印多久之前的数据:分钟 + internal static double SaveHisDataDays;//只保留最近几天的打印数据:天 + } +} \ No newline at end of file diff --git a/POSCorePrint/Content.cs b/POSCorePrint/Content.cs new file mode 100644 index 0000000..965cc0f --- /dev/null +++ b/POSCorePrint/Content.cs @@ -0,0 +1,55 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace POSCorePrint +{ + /// + /// 打印机 + /// + internal class Content + { + /// + /// ID + /// + public string ID { get; set; } + /// + /// 打印的内容 + /// + public string ContentVal { get; set; } + + /// + /// 通过Socket方式判断打印机是否正常连接 + /// + /// + /// + /// + //internal static bool IsConnect(string ip, int port) + //{ + // //Task.Delay(10); + // var isConnect = false; + // IPAddress iPAddress = IPAddress.Parse(ip); + // IPEndPoint iPEndPoint = new IPEndPoint(iPAddress, port); + // Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); + // try + // { + // IAsyncResult asyncResult = socket.BeginConnect(iPEndPoint, null, null); + // asyncResult.AsyncWaitHandle.WaitOne(2000, true); + // if (asyncResult.IsCompleted) + // isConnect = true; + // LogHelper.Log().Warn($"打印机 {ip}" + (isConnect ? "正常" : "离线!")); + // } + // catch (Exception ex) + // { + // ILog log = LogHelper.Log(); + // Console.WriteLine($" {ip} 判断打印机是否正常连接失败:【{ex.Message}】",ex); + // } + // finally + // { + // socket.Close(); + // } + // return isConnect; + //} + + } +} \ No newline at end of file diff --git a/POSCorePrint/Jobs/PrintJob.cs b/POSCorePrint/Jobs/PrintJob.cs new file mode 100644 index 0000000..b8b00f7 --- /dev/null +++ b/POSCorePrint/Jobs/PrintJob.cs @@ -0,0 +1,196 @@ + +using MySql.Data.MySqlClient; +using Quartz; +using System; +using System.Collections; +using System.Collections.Generic; +using System.Data; +using System.Net; +using System.Net.Sockets; +using System.Text; +using System.Threading.Tasks; +using System.Linq; +using System.Threading; + +namespace POSCorePrint.Jobs +{ + /// + /// edit by liuwz 2019-09-18 修改防止重复打印的方法 + /// 打印任务 + /// + [DisallowConcurrentExecution] + public class PrintJob : IJob + { + private readonly ManualResetEvent TimeoutObject = new ManualResetEvent(false); + private int Timeout = 1500;//连接打印机超时时间,毫秒。 + private int TimeoutSend = 5000;//连接打印机超时时间,毫秒。 + + public Task Execute(IJobExecutionContext context) + { + //查询最近N分钟需要打印的数据,同时删除N天前的打印数据。 + string sql = $@"select task.Id,task.content,print.PrintIP from pos_store_printtask task inner join pos_store_print print on task.code=print.Id + where task.status = 0 and print.isdelete=0 and task.CreateTime >'{DateTime.Now.AddMinutes(-Constant.PrintBackTime).ToString("yyyy-MM-dd HH:mm:ss")}'; + delete from pos_store_printtask where CreateTime <'{DateTime.Now.AddDays(-Constant.SaveHisDataDays).ToString("yyyy-MM-dd HH:mm:ss")}';"; + // TODO 查询数据库获取打印任务列表 + DataTable dataTable; + try + { + dataTable = MySqlHelper.ExecuteDataset(Constant.ConnectionString, sql).Tables[0]; + //无打印任务 + if (dataTable.Rows.Count == 0) + return Task.CompletedTask; + //有打印任务,按打印机分组 + var dicPrints = new Dictionary>(); + foreach (DataRow row in dataTable.Rows) + { + var newContent = new Content() { ID = row["Id"].ToString(), ContentVal = row["Content"].ToString() }; + if (!dicPrints.ContainsKey(row["PrintIP"].ToString())) + { + dicPrints.Add( + row["PrintIP"].ToString(), + new List { newContent } + ); + } + else + dicPrints[row["PrintIP"].ToString()].Add(newContent); + } + //按打印机并行异步发送 + Console.WriteLine($"{DateTime.Now.ToString()}:打印开始"); + List taskList = new List(); + foreach (KeyValuePair> p in dicPrints) + { + taskList.Add(Send(p)); + } + //等所有异步打印完再返回 + Task.WhenAll(taskList).Wait(); + Console.WriteLine($"{DateTime.Now.ToString()}:打印结束"); + return Task.CompletedTask; + } + catch (Exception ex) + { + Console.WriteLine($"{DateTime.Now.ToString()}:获取打印任务失败", ex); + return Task.CompletedTask; + } + } + + /// + /// 异步发送打印任务 + /// + /// + private async Task Send(KeyValuePair> p) + { + string idSuccess = ""; + string idFail = ""; + await Task.Run(() => + { + IPAddress iPAddress = IPAddress.Parse(p.Key); + IPEndPoint iPEndPoint = new IPEndPoint(iPAddress, Constant.port); + Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); + try + { + TimeoutObject.Reset(); + socket.BeginConnect(iPEndPoint, CallBackMethod,socket); + //阻塞当前线程              + if (!TimeoutObject.WaitOne(Timeout, false)) + { + //处理连接不成功的动作 + socket.Close(); + Console.WriteLine($"{DateTime.Now.ToString()}: 打印失败:{p.Key}【连接超时】"); + return; + } + if (!socket.Connected) + { + //处理连接不成功的动作 + socket.Close(); + Console.WriteLine($"{DateTime.Now.ToString()}: 打印失败:{p.Key} socket【连接超时】"); + return; + } +{ foreach (Content content in p.Value) + { + ////判断打印机状态:是否在线,是否有纸 + //byte[] dataSatus = new byte[3]; + //for (int i = 0; i < dataSatus.Length; i++) + //{ + // dataSatus[i] = byte.Parse(content.ContentVal.Substring(i * 2, 2), System.Globalization.NumberStyles.HexNumber); + //} + //int iRes = socket.Send(dataSatus, 0); + + //打印内容 + byte[] data = new byte[content.ContentVal.Length / 2]; + try + { + for (int i = 0; i < data.Length; i++) + { + data[i] = byte.Parse(content.ContentVal.Substring(i * 2, 2), System.Globalization.NumberStyles.HexNumber); + } + } + catch (Exception ex) + { + Console.WriteLine($"{DateTime.Now.ToString()}:转换打印数据失败 {ex.Message}", ex); + } + + try + { + //byte[] mData = new byte[data.Length]; + //Array.Copy(data, 0, mData, 0, data.Length); + SocketError errorCode; + //edit by liuwz 2020-04-03 增加超时 + socket.SendTimeout= TimeoutSend; + + int iResult = socket.Send(data, 0, out errorCode); + //add by liuwz 2020-01-13增加打印发送长度判断。 + if (SocketError.Success != errorCode || iResult !=data.Length) + { + //add by liuwz 2019-11-29失败时增加打印次数。 + idFail += string.IsNullOrEmpty(idFail) ? $"'{content.ID}'" : $",'{content.ID}'"; + Console.WriteLine($"{DateTime.Now.ToString()}: ip:{p.Key},id:{content.ID} 打印返回失败:【{errorCode.ToString()}】"); + } + else + idSuccess += string.IsNullOrEmpty(idSuccess) ? $"'{content.ID}'" : $",'{content.ID}'"; + } + catch (Exception ex) + { + //add by liuwz 2019-11-29失败时增加打印次数。 + idFail += string.IsNullOrEmpty(idFail) ? $"'{content.ID}'" : $",'{content.ID}'"; + Console.WriteLine($"{DateTime.Now.ToString()}: ip:{p.Key},id:{content.ID} 发送打印命令失败:【{ex.Message}】", ex); + } + } + + // 操作数据库更新状态 + if (!string.IsNullOrEmpty(idSuccess)) + { + var sql = $"update pos_store_printtask set status=1,PrintTime=NOW(),PrintTimes=ifnull(PrintTimes,0)+1 where Id in({idSuccess});"; + Console.WriteLine($"{DateTime.Now.ToString()}:打印成功更新记录:{iPAddress};{idSuccess}"); + + MySqlHelper.ExecuteNonQueryAsync(Constant.ConnectionString, sql); + } + if (!string.IsNullOrEmpty(idFail)) + { + Console.WriteLine($"{DateTime.Now.ToString()}:打失败更新记录:{iPAddress};{idFail}"); + var sql = $"update pos_store_printtask set PrintTimes=ifnull(PrintTimes,0)+1 where Id in({idFail});"; + MySqlHelper.ExecuteNonQueryAsync(Constant.ConnectionString, sql); + } + } + catch (Exception ex) + { + Console.WriteLine($"{DateTime.Now.ToString()}: 打印失败:{p.Key}【{ex.Message}】{string.Join('|',p.Value.Select(m=>new {m.ID}))}", ex); + } + finally + { + socket.Close(); + } + }); + } + + /// + /// 异步回调方法  + /// + /// + private void CallBackMethod(IAsyncResult asyncresult) + { + //使阻塞的线程继续           + TimeoutObject.Set(); + } + + } +} diff --git a/POSCorePrint/POSCorePrint.csproj b/POSCorePrint/POSCorePrint.csproj new file mode 100644 index 0000000..8737840 --- /dev/null +++ b/POSCorePrint/POSCorePrint.csproj @@ -0,0 +1,35 @@ + + + + Exe + netcoreapp2.2 + + + win-x86;win-x64;linux-x64;osx-x64 + 7.1 + + + + 3 + false + AnyCPU + true + + + + + + + + + + + + + PreserveNewest + + + + + + diff --git a/POSCorePrint/Program.cs b/POSCorePrint/Program.cs new file mode 100644 index 0000000..fb0fd30 --- /dev/null +++ b/POSCorePrint/Program.cs @@ -0,0 +1,73 @@ + +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using System; +using System.Collections.Generic; +using System.Globalization; +using System.IO; +using System.Threading; +using System.Threading.Tasks; + +namespace POSCorePrint +{ + class Program + { + //private static Dictionary printerList = new Dictionary(); + + static async Task Main(string[] args) + { + //add by liuwz 2019-12-26 解决linux下时间格式的问题 + var culture = CultureInfo.CreateSpecificCulture("zh-CN"); + var dateformat = new DateTimeFormatInfo() + { + ShortTimePattern = "HH:mm:ss", + LongTimePattern = "HH:mm:ss", + ShortDatePattern = "yyyy-MM-dd", + LongDatePattern = "yyyy-MM-dd", + FullDateTimePattern = "yyyy-MM-dd HH:mm:ss" + }; + culture.DateTimeFormat = dateformat; + CultureInfo.DefaultThreadCurrentUICulture = culture; + CultureInfo.DefaultThreadCurrentCulture = culture; + + var builder = new ConfigurationBuilder().SetBasePath(Directory.GetCurrentDirectory()).AddJsonFile("appsettings.json", optional: false, reloadOnChange: true); + Constant.Configuration = builder.Build(); + Constant.ConnectionString = Constant.Configuration["Config:connectionString"]; + Constant.PrintBackTime = double.Parse(Constant.Configuration["taskTimes:printBackTime"]); + Constant.SaveHisDataDays = double.Parse(Constant.Configuration["taskTimes:saveHisDataDays"]); + + + + var hostBuilder = new HostBuilder() + .ConfigureServices(serviceCollection => + { + //注册我们的服务接口 + serviceCollection.AddSingleton(); + }); + await hostBuilder.RunConsoleAsync(); + } + } + + class MyService : IHostedService + { + public Task StartAsync(CancellationToken cancellationToken) + { + return Task.Run(() => + { + Console.WriteLine($"{DateTime.Now.ToString()}:开启打印任务:" + DateTime.Now.ToString()); + + TaskStart taskStart = new TaskStart(); + taskStart.Start(); + }); + } + + public Task StopAsync(CancellationToken cancellationToken) + { + return Task.Run(() => + { + Console.WriteLine($"{DateTime.Now.ToString()}:关闭打印任务:" + DateTime.Now.ToString()); + }); + } + } +} diff --git a/POSCorePrint/Properties/PublishProfiles/FolderProfile.pubxml b/POSCorePrint/Properties/PublishProfiles/FolderProfile.pubxml new file mode 100644 index 0000000..9955b61 --- /dev/null +++ b/POSCorePrint/Properties/PublishProfiles/FolderProfile.pubxml @@ -0,0 +1,15 @@ + + + + + FileSystem + Release + Any CPU + netcoreapp2.2 + D:\publish\core_posprint + false + <_IsPortable>true + + \ No newline at end of file diff --git a/POSCorePrint/TaskStart.cs b/POSCorePrint/TaskStart.cs new file mode 100644 index 0000000..300cdbb --- /dev/null +++ b/POSCorePrint/TaskStart.cs @@ -0,0 +1,52 @@ +using Quartz; +using Quartz.Impl; +using System; +using System.Collections.Specialized; +using System.Threading.Tasks; +using POSCorePrint.Jobs; +using System.Net; +using System.Net.Sockets; + +namespace POSCorePrint +{ + public class TaskStart + { + public string PrintTaskTime { get; } = Constant.Configuration["taskTimes:printTaskTime"]; + public async Task Start() + { + try + { + // 从工厂中获取调度程序实例 + NameValueCollection props = new NameValueCollection + { + { "quartz.serializer.type", "binary" } + }; + StdSchedulerFactory factory = new StdSchedulerFactory(props); + IScheduler scheduler = await factory.GetScheduler(); + + // 开启调度器 + await scheduler.Start(); + + #region//打印任务 + IJobDetail job = JobBuilder.Create() + .WithIdentity("PrintJob", "group") + .Build(); + + // 定时获取打印任务 + ITrigger trigger = TriggerBuilder.Create() + .WithIdentity("PrintTrigger", "group") + .WithCronSchedule(PrintTaskTime) + .Build(); + + // 告诉Quartz使用我们的触发器来安排作业 + await scheduler.ScheduleJob(job, trigger); + #endregion + + } + catch (SchedulerException se) + { + await Console.Error.WriteLineAsync($"{DateTime.Now.ToString()}:{se.ToString()}"); + } + } + } +} \ No newline at end of file diff --git a/POSCorePrint/appsettings.json b/POSCorePrint/appsettings.json new file mode 100644 index 0000000..d887618 --- /dev/null +++ b/POSCorePrint/appsettings.json @@ -0,0 +1,12 @@ +{ + "Config": { + "connectionString": "server=192.168.1.221;port=3306;uid=root;pwd=dlpos;database=posclient;charset=utf8;Allow User Variables=true;" + }, + "taskTimes": { + "printTaskTime": "0/3 * * * * ? *", + //ӡ֮ǰݣ + "printBackTime": 30, + //ֻĴӡݣ + "saveHisDataDays": 3 + } +} \ No newline at end of file diff --git a/readme.txt b/readme.txt new file mode 100644 index 0000000..e69de29