应用案例
企业要求实现通过ReportingService将报表的内容以手机短信的方式发送到订阅者的手机上进行浏览。
其中短信发送功能已经实现(提供了WebService,在这里不做讨论)
因为以短信的方式发送,暂时用ReportingServices的CSV格式采集数据。
一.需求分析
其中短信发送功能已经实现(提供了WebService,在这里不做讨论)
因为以短信的方式发送,暂时用ReportingServices的CSV格式采集数据。
订阅用的参数,在这里主要用到了一个手机号,为了说明简单,这里只让用户输入一个手机号
二.程序设计
namespace Lnnmc.ReportingServices.ShortMessageDeliveryProvider
1.记录用户订阅信息的SubscriptionData类
string Handset{get;} : 用户手机号 (不附合要求,该功能去掉)
string[] Groups{get;} : 当前报表订阅的组名
string[] Users{get;} : 当前订阅的用户名
void FromSettings(Setting[] settings) : 从settings中解释出用户订阅数据
Setting[] ToSettingArray() : 将用户订阅数据生成setting数组
2.实现IExtension, IDeliveryExtension的ShortMessageProvider类
string LocalizedName{get;} : 在RS的传递下拉框中显示的名称
void SetConfiguration(string configuration) : 从rsreportserver.config中得到当前Delivery的配置信息
Setting[] ExtensionSettings : 定义服务器端的默认Setting配置
IDeliveryReportServerInformation ReportServerInformation : 可以得到当前RS服务器上的一些可用的Render类型
bool Deliver(Notification notification) : 这个方法就是用来执行发送的
Setting[] ValidateUserData(Setting[] settings) : 验证用户输入的setting是否有效
3.实现UI接口WebControl, ISubscriptionBaseUIUserControl, IExtension的ShortMessageUIProvider类
string LocalizedName{get;} : 显示在报表传递属性中的传递者下拉框中
void SetConfiguration(string configuration) : 从RSWebApplication.config得到UI的配置信息
string Description{get;} : 订阅后,显示在订阅列表中的"描述"字段里
Setting[] UserData{get;set;} : 这个很关键,是得到UI上的用户配置数据(订阅时),得到原来的用户配置数据(编辑时)。
三.程序开发
SubscriptionData 源代码
using System.Collections.Generic;
using System.Collections;
using System.Text;
using System.Collections.Specialized;
using Microsoft.ReportingServices.Interfaces;
namespace Lnnmc.ReportingServices.ShortMessageDeliveryProvider
{
public class SubscriptionData
{
public string Handset = "";
public string[] Users = null;
public string[] UsersText = null;
public string[] Groups = null;
//public string Width = "5cm";
//public string High = "8cm";
internal const string HANDSET = "Handset";
internal const string USERS = "Users";
internal const string USERSTEXT = "UsersText";
internal const string GROUPS = "Groups";
//internal const string WIDTH = "Width";
//internal const string HIGH = "High";
public void FromSettings(Setting[] settings)
{
if (settings != null)
{
foreach (Setting s in settings)
{
switch (s.Name)
{
case HANDSET:
Handset = s.Value;
break;
case USERS:
if (s.Value != "")
{
Users = s.Value.Split('|');
}
break;
case GROUPS:
if (s.Value != "")
{
Groups = s.Value.Split('|');
}
break;
case USERSTEXT:
if (s.Value != null)
{
UsersText = s.Value.Split('|');
}
break;
//case WIDTH:
// this.Width = s.Value;
// break;
//case HIGH:
// this.High = s.Value;
// break;
default:
break;
}
}
}
}
public Setting[] ToSettingArray()
{
//Setting[] ss = new Setting[6];
Setting[] ss = new Setting[4];
ss[0] = CreateSetting(HANDSET, Handset);
if (Users != null)
{
ss[1] = CreateSetting(USERS, string.Join("|", Users));
}
else
{
ss[1] = CreateSetting(USERS, "");
}
if (Groups != null)
{
ss[2] = CreateSetting(GROUPS, string.Join("|", Groups));
}
else
{
ss[2] = CreateSetting(GROUPS, "");
}
if (UsersText != null)
{
ss[3] = CreateSetting(USERSTEXT, string.Join("|", UsersText));
}
else
{
ss[3] = CreateSetting(USERSTEXT, "");
}
//ss[4] = CreateSetting(WIDTH, this.Width);
//ss[5] = CreateSetting(HIGH, this.High);
return ss;
}
public override string ToString()
{
string info = "";
info += "total:";
info += "\r\n";
info += "users : " + (this.Users == null ? "0" : this.Users.Length.ToString());
info += "\r\n";
info += "groups: " + (this.Groups == null ? "0" : this.Groups.Length.ToString());
info += "\r\n";
info += "Data";
info += "\r\n";
info += "Users = " + (this.Users == null ? "" : string.Join("|", this.Users));
info += "\r\n";
info += "Groups = " + (this.Groups == null ? "" : string.Join("|", this.Groups));
//info += "\r\n";
//info += "Wigth=" + this.Width;
//info += "\r\n";
//info += "High=" + this.High;
return info;
}
public static Setting CreateSetting(string name, string value)
{
Setting s = new Setting();
s.Name = name;
s.Value = value;
return s;
}
}
}
ShortMessageProvider源代码
#define IMAGE
#undef IMAGE
#define EXCEL
using System;
using System.Xml;
using System.Collections.Generic;
using System.Text;
using System.IO;
using Microsoft.ReportingServices.Interfaces;
using System.Drawing;
using System.Drawing.Imaging;
using System.Data;
using System.Data.SqlClient;
using Microsoft.Office.Interop.Excel;
using System.Windows.Forms;
namespace Lnnmc.ReportingServices.ShortMessageDeliveryProvider
{
public class ShortMessageProvider
: IExtension, IDeliveryExtension
{
string m_smsSenderToolWebServiceUrl = "http://www.nmc.ln.cmcc/NSPS/SMSSendService/SMSSender.asmx";
string m_smsLogFilePath = @"c:\csharp\log\short_msg_report_{0}.log";
string m_smsChannel = "02468100";
string m_connStr = "";
string m_reportMmsParse = "";
int m_maxRow = 500;
int m_maxColumn = 50;
// Represents an extension in SQL Server Reporting Services.
IExtension
// Represents a delivery extension in SQL Server Reporting Services
IDeliveryExtension
public int SendSMS(Report report, SubscriptionData subscripton)
{
string msg = "";
string reportName = report.Name;
string reportTime = report.Date.ToString("HH:mm");
msg += "网事知多少之" + reportName;
string[] subscribers = GetAllMobile(subscripton);
if (subscribers != null && subscribers.Length > 0)
{
#if CSV
msg += "\r\n";
string deviceInfo = "<DeviceInfo><Encoding>Unicode</Encoding></DeviceInfo>";
RenderedOutputFile[] m_files = report.Render("CSV", deviceInfo);
if (m_files.Length > 0)
{
Stream stream = m_files[0].Data;
StreamReader reader = new StreamReader(stream);
reader.BaseStream.Position = 0;
string reportString = reader.ReadToEnd();
reader.Close();
stream.Close();
// 得到报表数据
if (!string.IsNullOrEmpty(reportString) && (reportString.Length < (100 * 1024)))
{
SmsSenderToolWS.SMSSender smsSender = new SmsSenderToolWS.SMSSender();
smsSender.Url = m_smsSenderToolWebServiceUrl;
string receiver = subscripton.Handset;
string logFile = string.Format(m_smsLogFilePath, DateTime.Now.ToString("yyyyMMdd"));
bool success = false;
msg += reportString;
// 发送短信
success = smsSender.SMSSenderTool(logFile,
m_smsChannel,
"",
receiver,
msg,
8);
}
}
else
{
// 无可用数据,不发送
}
#endif
#if IMAGE
string deviceInfo = "<DeviceInfo><OutputFormat>EMF</OutputFormat><PageHeight>" + subscripton.High + "</PageHeight><PageWidth>" + subscripton.Width + "</PageWidth><DpiX>0.1cm</DpiX><DpiY>0.0cm</DpiY><MarginLeft>0.0cm</MarginLeft><MarginRight>0.0cm</MarginRight><MarginTop>0.0cm</MarginTop><MarginBottom>0.0cm</MarginBottom></DeviceInfo>";
RenderedOutputFile[] m_files = report.Render("IMAGE", deviceInfo);
WriteLog("SendSMS() m_files.Length=" + m_files.Length.ToString());
int pageCount = m_files.Length;
if (pageCount > 0)
{
MMSWebService.MMSItem[] items = new MMSWebService.MMSItem[pageCount];
int index = 1;
foreach (RenderedOutputFile rf in m_files)
{
Stream stream = rf.Data;
WriteLog("SendSMS() stream.Length=" + stream.Length.ToString());
///////////////////////////////////////////////////////////////////
stream.Position = 0;
MemoryStream ms = new MemoryStream();
Image img = Image.FromStream(stream);
MemoryStream ms = new MemoryStream();
img.Save(ms, ImageFormat.Jpeg);
byte[] buffer = ms.ToArray();
items[index - 1] = new MMSWebService.MMSItem();
items[index - 1].Content = buffer;
items[index - 1].Discription = "";
items[index - 1].Type = "jpg";
///////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////
stream.Position = 0;
Metafile mf = new Metafile(stream);
// mf.Save(@"c:\Work\ReportingServices\Out\" + DateTime.Now.ToString("MM_dd_HH_mm") + "_" + index.ToString() + ".jpg");
stream.Close();
///////////////////////////////////////////////////////////////////
index++;
/*
// 加上一个页数说明
string pageInfo = index.ToString() + "/" + pageCount.ToString();
items[index - 1] = new MMSWebService.MMSItem();
items[index - 1].Content = System.Text.Encoding.UTF8.GetBytes(pageInfo);
items[index - 1].Discription = "";
items[index - 1].Type = "txt";
index++;
*/
}
MMSWebService.MMSService mms = new MMSWebService.MMSService();
MMSWebService.Message mmsMsg = new MMSWebService.Message();
mmsMsg.AppendId = "00";
mmsMsg.From = m_smsChannel;
mmsMsg.Header = "";
mmsMsg.Panel = m_smsChannel;
mmsMsg.Subject = msg;
// 得到所有订阅者手机号
mmsMsg.To = string.Join(";", subscribers);
mmsMsg.Body = items;
mms.Send(mmsMsg);
}
#endif
#if EXCEL
WriteLog("开始生成Excel文件");
string deviceInfo = "<DeviceInfo></DeviceInfo>"; //<OmitDocumentMap>false</OmitDocumentMap><OmitFormulas>false</OmitFormulas><RemoveSpace>0.125in</RemoveSpace>
RenderedOutputFile[] m_files = report.Render("EXCEL", deviceInfo);
WriteLog("Excel文件个数=" + m_files.Length.ToString());
foreach (RenderedOutputFile rf in m_files)
{
// 创建文件名
string fileName = Environment.GetEnvironmentVariable("TMP").TrimEnd('\\') + "\\" + "Report_" + DateTime.Now.ToString("yyyyMMddHHmmss") + ".xls";
WriteLog(fileName);
FileStream fs = new FileStream(fileName, FileMode.OpenOrCreate);
Stream stream = rf.Data;
stream.Position = 0;
BinaryReader br = new BinaryReader(stream);
br.BaseStream.Position = 0;
byte b;
//WriteLog("开始读文件流");
for (int rr = 0; rr < br.BaseStream.Length; rr++)
{
b = br.ReadByte();
fs.WriteByte(b);
}
//WriteLog("流数据读取完毕");
br.Close();
fs.Close();
fs.Dispose();
fs = null;
stream.Close();
stream.Dispose();
stream = null;
GC.Collect();
System.Threading.Thread.Sleep(2000);
try
{
System.Diagnostics.Process process = new System.Diagnostics.Process();
System.Diagnostics.ProcessStartInfo startInfo
= new System.Diagnostics.ProcessStartInfo();
startInfo.FileName = m_reportMmsParse;
startInfo.Arguments = "\"" + fileName
+ "\" \""
+ string.Join(";", subscribers)
+ "\" \""
+ m_smsLogFilePath
+ "\" \""
+ m_smsChannel
+ "\" \""
+ msg
+ "\" \""
+ m_maxRow.ToString()
+ "\" \""
+ m_maxColumn.ToString()
+ "\"";
startInfo.CreateNoWindow = false;
startInfo.UseShellExecute = true;
startInfo.RedirectStandardError = false;
process.StartInfo = startInfo;
process.Start();
process.WaitForExit();
startInfo = null;
process.Dispose();
process = null;
}
catch (Exception pppp)
{
WriteLog(pppp.ToString());
throw pppp;
}
finally
{
try
{
File.Delete(fileName);
}
catch
{ }
}
}
#endif
return subscribers.Length;
}
else
{
return 0;
}
}
private System.Security.SecureString ConvertToSecureString(string str)
{
System.Security.SecureString secureString = new System.Security.SecureString();
foreach (char c in str)
{
secureString.AppendChar(c);
}
return secureString;
}
public string[] GetAllMobile(SubscriptionData data)
{
string selectUser = "";
string selectGroup = "";
if (data.Users != null)
{
selectUser = @"select mobile from nsps_user_final where samaccountname in ('" + string.Join("','", data.Users) + "')";
}
else
{
selectUser = "select mobile from nsps_user_final where 1=0";
}
if (data.Groups != null)
{
selectGroup = @"select mobile from nsps_group_final g, nsps_user_final u, nsps_group_user_final f where f.userid=u.id and f.groupid=g.id and g.name in ('" + string.Join("','", data.Groups) + "')";
}
else
{
selectGroup = @"select '' mobile from nsps_group_final where 1=0";
}
SqlConnection conn = new SqlConnection(m_connStr);
conn.Open();
SqlCommand myCommand = conn.CreateCommand();
myCommand.CommandText = selectUser + " union " + selectGroup;
SqlDataReader userReader = myCommand.ExecuteReader();
int i = 0;
string temp = "";
while (userReader.Read())
{
i++;
temp += userReader[0].ToString();
temp += ",";
}
temp = temp.TrimEnd(',');
userReader.Close();
userReader = null;
conn.Close();
conn.Dispose();
conn = null;
return temp.Split(',');
}
public static void WriteLog(string sb)
{
return;
try
{
FileStream fs = new FileStream("c:\\ShortMessageDeliveryLog.txt", FileMode.Append,
FileAccess.Write);
StreamWriter writer = new StreamWriter(fs);
writer.WriteLine("[" + DateTime.Now.ToString("HH:mm:ss:ffff") + "]" + sb);
writer.Flush();
writer.Close();
}
catch (Exception ex)
{
throw new IOException("Error writing to log file: " + ex.Message);
}
}
}
}
ShortMessageUIProvider源代码
using System.Collections.Generic;
using System.Text;
using Microsoft.ReportingServices.Interfaces;
using System.Web.UI.WebControls;
using System.Web.UI.HtmlControls;
using System.Xml;
using System.Data;
using System.Data.SqlClient;
namespace Lnnmc.ReportingServices.ShortMessageDeliveryProvider
{
public class ShortMessageUIProvider
: System.Web.UI.WebControls.WebControl, ISubscriptionBaseUIUserControl, IExtension
{
string m_connStr = "";
//string m_screenSize = "";
public ShortMessageUIProvider()
{
this.Init +=new EventHandler(MyDeliveryUIProvider_Init);
this.Load += new EventHandler(ShortMessageUIProvider_Load);
this.PreRender +=new EventHandler(ShortMessageUIProvider_PreRender);
}
IExtension
ISubscriptionBaseUIUserControl
Controls
Event
public string[] GetAllMobile(SubscriptionData data)
{
string selectUser = "";
string selectGroup = "";
if (data.Users != null)
{
selectUser = @"select mobile from nsps_user_final where samaccountname in ('" + string.Join("','", data.Users) + "')";
}
else
{
selectUser = "select mobile from nsps_user_final where 1=0";
}
if (data.Groups != null)
{
selectGroup = @"select mobile from nsps_group_final g, nsps_user_final u, nsps_group_user_final f where f.userid=u.id and f.groupid=g.id and g.name in ('" + string.Join("','", data.Groups) + "')";
}
else
{
selectGroup = @"select mobile from nsps_group_final where 1=0";
}
SqlConnection conn = new SqlConnection(m_connStr);
conn.Open();
SqlCommand myCommand = conn.CreateCommand();
myCommand.CommandText = selectUser + " union " + selectGroup;
SqlDataReader userReader = myCommand.ExecuteReader();
int i = 0;
string temp = "";
while (userReader.Read())
{
i++;
temp += userReader[0].ToString();
temp += ",";
}
temp = temp.TrimEnd(',');
userReader.Close();
userReader = null;
conn.Close();
conn.Dispose();
conn = null;
return temp.Split(',');
}
}
}
实际开发过程中发现,Excel的一些处理只能在WinForm程序中进行(具体为啥没搞清楚)所以,在处理生成的Excel时,只能单独做了一个WinApp
ReportMmsParser.exe 的源代码
using System.Collections.Generic;
using System.Windows.Forms;
namespace ReportMmsParser
{
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main(string[] args)
{
if (args.Length != 7)
{
Usage();
}
else
{
try
{
// Application.EnableVisualStyles();
// Application.SetCompatibleTextRenderingDefault(false);
string fileName = args[0];
string receivers = args[1];
string m_smsLogFilePath = args[2];
string m_smsChannel = args[3];
string m_subject = args[4];
string m_maxRow = args[5];
string m_maxColumn = args[6];
Form1 frm = new Form1();
frm.txtChannel.Text = m_smsChannel;
frm.txtFilePath.Text = fileName;
frm.txtReceivers.Text = receivers;
frm.txtSmsLog.Text = m_smsLogFilePath;
frm.txtSubject.Text = m_subject;
frm.txtMaxRow.Text = m_maxRow;
frm.txtMaxColumn.Text = m_maxColumn;
Application.Run(frm);
}
catch(Exception eee)
{
}
}
}
public static void Usage()
{
Console.WriteLine("报表解析发送程序");
Console.WriteLine("输入4个参数");
Console.WriteLine("参数1:ReportService产生的Excel文件");
Console.WriteLine("参数2:接收者的手机号(逗号分隔)");
Console.WriteLine("参数3:短信日志文件路径");
Console.WriteLine("参数4:发短信时使用的通道号");
}
}
}
using System.IO;
using System.Drawing.Imaging;
using System.Diagnostics;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using Microsoft.Office.Interop;
using Microsoft.Office.Interop.Excel;
namespace ReportMmsParser
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void cmdParse_Click(object sender, EventArgs e)
{
string fileName = txtFilePath.Text;
string receivers = txtReceivers.Text;
string m_smsLogFilePath = txtSmsLog.Text;
string m_smsChannel = txtChannel.Text;
string m_subject = txtSubject.Text;
Int32 m_maxRow = Int32.Parse(txtMaxRow.Text);
Int32 m_maxColumn = Int32.Parse(txtMaxColumn.Text);
// 定义采集内容
System.Collections.ArrayList arrItems
= new System.Collections.ArrayList();
// 解析文件
string strOutputText = "";
ApplicationClass excel
= new ApplicationClass();
Workbook workBook = excel.Workbooks.Open(
fileName,
0,
true,
5,
"",
"",
true,
XlPlatform.xlWindows,
"\t",
false,
false,
0,
true,
1,
0);
Worksheet sheet = workBook.ActiveSheet as Worksheet;
bool finished = false;
int currentRow, currentColumn;
for (int rowIndex = 1; rowIndex <= m_maxRow; rowIndex++)
{
for (int colIndex = 1; colIndex <= m_maxColumn; colIndex++)
{
// 图片位置?
currentRow = rowIndex;
currentColumn = colIndex;
// 当前的单元格
Range range = sheet.Cells[rowIndex, colIndex] as Range;
if (range != null && range.Value2 != null)
{
string v = range.Value2.ToString();
if (v.IndexOf("------", 0) == 0) // 这个表示Item的分隔
{
if (strOutputText != "")
{
MMSWebService.MMSItem item = new MMSWebService.MMSItem();
item.Content = System.Text.Encoding.UTF8.GetBytes(strOutputText.Replace("\\r", "\r").Replace("\\n", "\n"));
item.Type = "txt";
arrItems.Add(item);
strOutputText = "";
}
}
else if (string.Compare(v, "end", true) == 0)
{
if (strOutputText != "")
{
MMSWebService.MMSItem item = new MMSWebService.MMSItem();
item.Content = System.Text.Encoding.UTF8.GetBytes(strOutputText.Replace("\\r", "\r").Replace("\\n", "\n"));
item.Type = "txt";
arrItems.Add(item);
strOutputText = "";
}
finished = true;
break;
}
else
{
strOutputText += v;
}
}
else // 如果当前单元格数据为空,那么看看这个格子是不是图片
{
foreach (Shape shape in sheet.Shapes)
{
if (shape.TopLeftCell.Row == currentRow
&& shape.TopLeftCell.Column == currentColumn)
{
// 找到图片了
// 先把前面的文字信息输出
if (strOutputText != "")
{
MMSWebService.MMSItem item = new MMSWebService.MMSItem();
item.Content = System.Text.Encoding.UTF8.GetBytes(strOutputText.Replace("\\r", "\r").Replace("\\n", "\n"));
item.Type = "txt";
arrItems.Add(item);
strOutputText = "";
}
shape.CopyPicture(XlPictureAppearance.xlScreen, XlCopyPictureFormat.xlBitmap);
if (Clipboard.ContainsImage())
{
Image imgBmp = Clipboard.GetData(System.Windows.Forms.DataFormats.Bitmap) as Image;
if (imgBmp != null)
{
// 转成jpg的
System.Drawing.Image imgJpeg = imgBmp.GetThumbnailImage(imgBmp.Width, imgBmp.Height, null, new IntPtr());
Graphics g = Graphics.FromImage(imgJpeg);
g.DrawImage(imgJpeg, 0, 0, imgJpeg.Width, imgJpeg.Height); //將原圖畫到指定的圖上
g.Dispose();
// Stream jpgStream = null ;
// imgJpeg.Save(jpgStream, ImageFormat.Jpeg);
MemoryStream ms = new MemoryStream();
imgJpeg.Save(ms, ImageFormat.Jpeg);
byte[] bf = ms.ToArray();
MMSWebService.MMSItem item = new MMSWebService.MMSItem();
item.Content = bf;
item.Discription = "";
item.Type = "jpg";
arrItems.Add(item);
}
}
else
{
}
}
}
}
if (finished)
{
break;
}
} // Exist for
}
// 有可能用户忘记写"end"了。所以要判断一下。
if (strOutputText != "")
{
MMSWebService.MMSItem item = new MMSWebService.MMSItem();
item.Content = System.Text.Encoding.UTF8.GetBytes(strOutputText.Replace("\\r", "\r").Replace("\\n", "\n"));
item.Type = "txt";
arrItems.Add(item);
strOutputText = "";
}
workBook.Close(false, "", 0);
workBook = null;
excel.Quit();
excel = null;
GC.Collect();
// 准备发
if (arrItems.Count > 0)
{
// 只发短信
if (arrItems.Count == 1)
{
MMSWebService.MMSItem item = arrItems[0] as MMSWebService.MMSItem;
if (item.Type == "txt")
{
string msgmsg = System.Text.Encoding.UTF8.GetString(item.Content);
SmsSenderToolWS.SMSSender smsSender = new SmsSenderToolWS.SMSSender();
string logFile = string.Format(m_smsLogFilePath, DateTime.Now.ToString("yyyyMMdd"));
bool success = false;
foreach (string receiver in receivers.Split(';'))
{
// 发送短信
success = smsSender.SMSSenderTool(logFile,
m_smsChannel,
"",
receiver,
msgmsg,
8);
}
}
}
else
{
MMSWebService.MMSService mms = new MMSWebService.MMSService();
MMSWebService.Message mmsMsg = new MMSWebService.Message();
mmsMsg.AppendId = "";
mmsMsg.From = m_smsChannel;
mmsMsg.Header = "";
mmsMsg.Panel = m_smsChannel;
mmsMsg.Subject = m_subject;
// 得到所有订阅者手机号
mmsMsg.To = receivers;
mmsMsg.Body = new MMSWebService.MMSItem[arrItems.Count];
for (int pp = 0; pp < arrItems.Count; pp++)
{
mmsMsg.Body[pp] = arrItems[pp] as MMSWebService.MMSItem;
}
mms.Send(mmsMsg);
}
}
}
private void Form1_Load(object sender, EventArgs e)
{
this.cmdParse_Click(null, null);
this.Close();
}
}
}
四.程序部署
五.程序测试
本次开发,参考了RS自带的PrinterDelivery例程