您好,欢迎访问一九零五行业门户网

仪器设备改造技术,实现测量数据上传到服务器的功能

怎样用c#进行仪器的设备改造?因为仪器控制程序是c#开发的,所以客户端最好是c#,考虑到节省流量(服务器是按流量收费的),文件要压缩,c#下要实现文件压缩功能。而服务端选择java构建restful api进行上传的的方案。
一、项目需求及分析按照领导的要求,要改造一台仪器,添加点功能,将测量数据上传到服务器。仪器测量节拍大概是20s,数据量目前不大,每次测量大概不到2m左右,且都是浮点数据和整形数据。
起初想用tcp长连接实现的,但考虑到现场环境。典型的制造业车间,电磁环境复杂,网络信号不稳,所以不考虑tcp长连接实现。短连接也不在考虑范围内,以后仪器数量多了之后频繁的建立连接开销也很大,服务器有可能受不了(阿里云的乞丐版)。所以选择用restful提交,http的通信可以多线程调度。
仪器控制程序是c#开发的,所以客户端最好是c#。服务端我想用springboot,很方便。
考虑到新增的上传功能不能影响之前的测量节拍,所以要多线程实现。可惜我又很懒,不想考虑线程协调问题,最后选择消息队列实现。
考虑到节省流量(服务器是按流量收费的),文件要压缩,c#下要实现文件压缩功能。
从测量文件中读取数据,将参数存入数据库,测量原始数据打包放在文件服务器上。
二、整体架构和技术方案最后的技术方案就是c#做客户端,java构建服务端restful api进行上传
整体架构如下图:
使用的技术如下:
c#的restful客户端:restsharp
java的restful服务端:springboot
c#端消息队列:netmq
c#端zip操作组件:dotnetzip
java端zip操作组件:apache commons compress
三、服务端服务端采用springboot的restful,post方式,非常简单。
传输文件采用multipartfile方式,因为客户端的resrsharp只能采用这种方式传递文件
@restcontroller@requestmapping(value = "upload")public class filerestcontroller { logger logger = logmanager.getlogger(filerestcontroller.class); @requestmapping(value = "file", method = requestmethod.post) public @responsebody restresult getzipfile(@requestparam("file") multipartfile file) throws ioexception, urisyntaxexception { restresult result = new restresult(); if (!file.getname().isempty()) { inputstream stream = file.getinputstream();// string directory = filerestcontroller.class.getprotectiondomain().getcodesource().getlocation().touri().getpath(); string directory = "/usr/local/haliang/files/"; try { directory = urldecoder.decode(directory, "utf-8"); } catch (java.io.unsupportedencodingexception e) { return null; } fileoutputstream fs = new fileoutputstream(directory + file.getoriginalfilename()); logger.info("文件所在的目录: " + directory + "/files/" + file.getoriginalfilename()); byte[] buffer = new byte[1024 * 1024]; int bytesum = 0; int byteread = 0; while ((byteread = stream.read(buffer)) != -1) { bytesum += byteread; fs.write(buffer, 0, byteread); fs.flush(); } fs.close(); stream.close(); logger.info("成功接收文件: " + directory + file.getoriginalfilename()); } return result; }}
四、客户端客户端架构如下图:
1 zeromq简介netmq 是 zeromq的c#移植版本。
1.1:zeromq是什么netmq (zeromq to .net),zmq号称史上最快中间件。
它对socket通信进行了封装,使得我们不需要写socket函数调用就能完成复杂的网络通信。
它跟socket的区别是:普通的socket是端到端的(1:1的关系),而zmq却是可以n:m的关系,人们对bsd套接字的了解较多的是点对点的连接,点对点连接需要显式地建立连接、销毁连接、选择协议(tcp/udp)和处理错误等,而zmq屏蔽了这些细节,让你的网络编程更为简单。
它是一个消息处理队列库,可在多个线程、内核和主机盒之间弹性伸缩。和一般意义上的消息队列产品不同的是,它没有消息队列服务器,而更像是一个网络通信库。从网络通信的角度看,它处于会话层之上,应用层之下,属于传输层。
1.2:zeromq的消息模型zeromq将消息通信分为4种模型,分别是一对一结对模型(exclusive-pair)、请求回应模型(request-reply)、发布订阅模型(publish-subscribe)、推拉模型(push-pull)。这4种模型总结出了通用的网络通信模型,在实际中可以根据应用需要,组合其中的2种或多种模型来形成自己的解决方案。
1.2.1 一对一结对模型 exclusive-pair最简单的1:1消息通信模型,用来支持传统的 tcp socket模型,主要用于进程内部线程间通信。可以认为是一个tcp connection,但是tcp server只能接受一个连接。采用了lock free实现,速度很快。数据可以双向流动,这点不同于后面的请求响应模型。(不推荐使用,没有例子)
1.2.2 请求回应模型 request-reply由请求端发起请求,然后等待回应端应答。一个请求必须对应一个回应,从请求端的角度来看是发-收配对,从回应端的角度是收-发对。跟一对一结对模型的区别在于请求端可以是1~n个。
请求端和回应端都可以是1:n的模型。通常把1认为是server,n认为是client。zeromq可以很好的支持路由功能(实现路由功能的组件叫作device),把1:n扩展为n:m(只需要加入若干路由节点)。从这个模型看,更底层的端点地址是对上层隐藏的。每个请求都隐含有回应地址,而应用则不关心它。通常把该模型主要用于远程调用及任务分配等。
(netmq请求响应c#调用案例)
1.2.3 发布订阅模型 publisher-subscriber(本项目采用的模型)发布端单向分发数据,且不关心是否把全部信息发送给订阅端。如果发布端开始发布信息时,订阅端尚未连接上来,则这些信息会被直接丢弃。订阅端未连接导致信息丢失的问题,可以通过与请求回应模型组合来解决。订阅端只负责接收,而不能反馈,且在订阅端消费速度慢于发布端的情况下,会在订阅端堆积数据。该模型主要用于数据分发。天气预报、微博明星粉丝可以应用这种经典模型。 (netmq发布订阅模式c#调用案例)
1.2.4 推拉模型 push-pullserver端作为push端,而client端作为pull端,如果有多个client端同时连接到server端,则server端会在内部做一个负载均衡,采用平均分配的算法,将所有消息均衡发布到client端上。与发布订阅模型相比,推拉模型在没有消费者的情况下,发布的消息不会被消耗掉;在消费者能力不够的情况下,能够提供多消费者并行消费解决方案。该模型主要用于多任务并行。
(netmq推拉模式c#调用案例)
1.3:zeromq的优势tcp:zeromq基于消息,消息模式,而非字节流。
xmpp:zeromq更简单、快速、更底层。jabber可建在zeromq之上。
amqp:完成相同的工作,zeromq要快100倍,而且不需要代理(规范更简洁——少278页)
ipc:zeromq可以跨多个主机盒,而非单台机器。
corba:zeromq不会将复杂到恐怖的消息格式强加于你。
rpc:zeromq完全是异步的,你可以随时增加/删除参与者。
rfc 1149:zeromq比它快多了!
29west lbm:zeromq是自由软件!
ibm低延迟:zeromq是自由软件!
tibco:仍然是自由软件!
2.代码实现2.1 publisher(发布者)一般都是发布者先启动,绑定监听端口。封装了一个发送函数,主要是发送原先软件生成测量文件的路径。
public class publisher { public int port { get; set; } private publishersocket socket; /// <summary> /// 构造函数 /// </summary> /// <param name="port">绑定的端口</param> public publisher(int port) { port = port; } /// <summary> /// 启动发布端 /// </summary> public void start() { netmqcontext context = netmqcontext.create(); this.socket = context.createpublishersocket(); this.socket.bind("tcp://127.0.0.1:" + port); } /// <summary> /// 发送数据 /// </summary> /// <param name="result"></param> public void send(string result) { socket.sendframe(result); } }
2.2 subscriber(订阅者)订阅者启动时候连接端口。防止线程阻塞,订阅者是新开一个线程运行的。
public class subscribe { private delegate void getdatahandler(string message); private event getdatahandler ongetdata; public int port { get; set; } public string tempdirectory { get; set; } public bool isrunning { get; set; } public string domain { get; set; } public subscribe(int port, string domain) { port = port; this.domain = domain; ongetdata += processdata; } private subscribersocket socket; public void start() { this.isrunning = true; netmqcontext context = netmqcontext.create(); socket = context.createsubscribersocket(); socket.connect("tcp://127.0.0.1:" + port); socket.subscribe(""); thread t = new thread(new threadstart(startsub)); t.start(); } private void startsub() { while (isrunning) { thread.sleep(10000); string result = socket.receiveframestring(encoding.utf8); ongetdata(result); } } private void processdata(string path) { console.writeline("收到文件:" + path); string compressedfile = compress.compressfile(tempdirectory, path); new restpost(domain).post(compressedfile); }
3 客户端压缩压缩使用dotnetzip组件,非常简单好用。
public class compress { public static string compressfile(string temp,string txtpath) { string txtfilename = system.io.path.getfilenamewithoutextension(txtpath); string compressedfilename = temp+"/"+txtfilename + ".zip"; zipfile file=new zipfile(); file.addfile(txtpath,""); file.save(compressedfilename); return compressedfilename; } }
4 客户端上传使用restsharp组件,也是非常简单。异步回调,不影响性能。
public class restpost { public string domain { get; set; } public restpost(string domain) { domain = domain; } public void post(string path) { restrequest request = new restrequest(method.post); request.addfile("file", path); restclient client = new restclient {baseurl = new uri("http://" + domain + "/upload/file")}; client.executeasync(request, (response) => { if (response.statuscode == httpstatuscode.ok) { console.writeline("上传成功...\n" + response.content); } else { console.writeline($"出错啦:{response.content}"); } } ); } }
五、总结写代码之前一定要搞清楚需求,设计好架构
注意消息队列启动时候的线程问题
异步执行
相关文章:
企业数据备份技术
相关视频:
数据结构探险—队列篇
以上就是仪器设备改造技术,实现测量数据上传到服务器的功能的详细内容。
其它类似信息

推荐信息