jbase仪器接口设计

jbase的计划有借助虚拟M来实现连仪器,之前陆续写了些TCP逻辑,今天终于整理完成了仪器设计。首先用java的cs程序测试TCP的服务和客户端。

javafx的示例加强

package sample;

import javafx.application.Application;
import javafx.event.EventHandler;
import javafx.fxml.FXMLLoader;
import javafx.geometry.Pos;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.FlowPane;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import javafx.scene.control.Button;
import javafx.scene.control.TextArea;
import javafx.scene.control.ScrollPane;

import java.awt.*;
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.atomic.AtomicInteger;

public class Main extends Application {

    //数字
    static int num = 0;

    //场景
    Scene scene;

    //场景1
    Scene scene1;

    //定时发送
    Socket sendSocket = null;

    //自动发送次数
    int sendNum = 0;

    //通讯日志
    StringBuilder sbLog=new StringBuilder();

    //换行符
    String lineStr=System.getProperty("line.separator");


    @Override
    public void start(Stage stage) throws Exception {
        //舞台标题
        stage.setTitle("第一个java程序");
        // 流式布局:按照控件的添加次序按个摆放,按照从上到下、从左到右的次序摆放。
        FlowPane pane = new FlowPane(5, 5);
        // 居中显示
        pane.setAlignment(Pos.CENTER);
        // 场景
        scene = new Scene(pane, 800, 600);


        // 标签
        Label label = new Label("初始值:" + num);


        // 加1按钮
        Button addButton = new Button("加1");
        addButton.setOnMouseClicked(e -> {
            num++;
            label.setText("当前值:" + num);
        });

        //减1按钮
        Button subButton = new Button("减1");
        subButton.setOnMouseClicked(e -> {
            num--;
            label.setText("当前值:" + num);
        });

        //切换到场景1
        Button btnScene1 = new Button("场景1");
        btnScene1.setOnMouseClicked(e -> {
            stage.setScene(scene1);
        });

        //弹出一个子窗口
        Button btnShowChildForm = new Button("子窗口");
        btnShowChildForm.setOnMouseClicked(e -> {
            Child.ShowChild("子窗口", "传给子窗口的参数");
        });

        // 创建一个TextArea控件
        TextArea textArea = new TextArea();
        textArea.setWrapText(true); // 设置文本自动换行

        //定时器,主动上传时候用
        Timer timer = new Timer();
        TimerTask task = new TimerTask() {
            @Override
            public void run() {
                if (sendSocket != null) {
                    try {
                        sendNum++;
                        // 发送请求消息到服务端
                        OutputStream outputStream = sendSocket.getOutputStream();
                        PrintWriter out = new PrintWriter(new OutputStreamWriter(outputStream, "GBK"), true);
                        out.println("这是定时发送的第" + sendNum + "次数据");
                        out.flush();
                    } catch (Exception ex) {

                    }
                }
            }
        };
        // 设置定时器的执行策略,延迟0秒后开始执行,每隔1秒执行一次
        timer.schedule(task, 0, 5000);


        //启动tcp服务
        Button btnTcpServer = new Button("TCP服务");
        btnTcpServer.setOnMouseClicked(e -> {
            Thread clientThread = new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        ServerSocket serverSocket = new ServerSocket(8888);
                        //增加一个无限循环
                        while (true) {
                            sbLog.append("服务启动"+lineStr);
                            System.out.println("服务启动");
                            //通知界面
                            javafx.application.Platform.runLater(()->{
                                textArea.setText(sbLog.toString());
                            });
                            //等待客户端连接,阻塞
                            Socket clientSocket = serverSocket.accept();
                            sendSocket = clientSocket;
                            //得到输出流
                            PrintWriter out = new PrintWriter(clientSocket.getOutputStream(), true);
                            System.out.println("客户端连接");
                            sbLog.append("客户端连接"+lineStr);
                            //通知界面
                            javafx.application.Platform.runLater(()->{
                                textArea.setText(sbLog.toString());
                            });
                            //得到输入流
                            InputStream inputStream = clientSocket.getInputStream();
                            //IO读取
                            byte[] buf = new byte[10240];
                            int readlen = 0;
                            //阻塞读取数据
                            while ((readlen = inputStream.read(buf)) != -1) {
                                System.out.println(new String(buf, 0, readlen, "GBK"));
                                sbLog.append(new String(buf, 0, readlen, "GBK")+lineStr);
                                //通知界面
                                javafx.application.Platform.runLater(()->{
                                    textArea.setText(sbLog.toString());
                                });
                            }
                            //关闭输入
                            inputStream.close();
                            //关闭输出
                            out.close();
                            clientSocket.close();
                        }
                    } catch (Exception e) {

                    }
                }
            });
            clientThread.start();
        });

        //启动tcp客户端
        Button btnTcpClient = new Button("TCP客户端");
        btnTcpClient.setOnMouseClicked(e -> {
            Thread clientThread = new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        try {
                            // 创建 Socket 对象并连接到服务端
                            Socket socket = new Socket("172.16.1.232", 1991);
                            sendSocket = socket;
                            // 发送请求消息到服务端
                            OutputStream outputStream = socket.getOutputStream();
                            PrintWriter out = new PrintWriter(new OutputStreamWriter(outputStream, "GBK"), true);
                            out.println("你好,我是客户端");
                            out.flush();
                            //得到输入流
                            InputStream inputStream = socket.getInputStream();
                            //IO读取
                            byte[] buf = new byte[10240];
                            int readlen = 0;
                            //阻塞读取数据
                            while ((readlen = inputStream.read(buf)) != -1) {
                                System.out.println(new String(buf, 0, readlen, "GBK"));
                                sbLog.append(new String(buf, 0, readlen, "GBK")+lineStr);
                                out.println("我已经收到数据");
                                //通知界面
                                javafx.application.Platform.runLater(()->{
                                    textArea.setText(sbLog.toString());
                                });
                            }
                            //关闭输入
                            inputStream.close();
                            //关闭输出
                            out.close();
                            // 关闭连接
                            socket.close();
                        } catch (IOException ex) {
                            ex.printStackTrace();
                        }
                    } catch (Exception e) {

                    }
                }
            });
            clientThread.start();
        });

        pane.getChildren().addAll(addButton, subButton, btnScene1, btnShowChildForm, label, textArea, btnTcpServer, btnTcpClient);


        // 场景1
        // 流式布局:按照控件的添加次序按个摆放,按照从上到下、从左到右的次序摆放。
        FlowPane pane1 = new FlowPane(10, 10);
        // 居中显示
        pane1.setAlignment(Pos.CENTER);
        scene1 = new Scene(pane1, 200, 150);

        //返回开始的场景
        Button btnReturn = new Button("返回");
        btnReturn.setOnMouseClicked(e -> {
            stage.setScene(scene);
        });
        pane1.getChildren().addAll(btnReturn);

        //默认场景和显示
        stage.setScene(scene);
        stage.show();
    }


    public static void main(String[] args) {
        launch(args);
    }
}

jbase仪器接口设计_第1张图片

客户端测试成功后设计jbase仪器基础,这里首先要理解连设备的本质,连TCP设备的话,其实真正的业务只关心我当做客户端还是服务端、以什么编码、然后解析和发送数据,我并不想关系TCP的实现细节。所以可以把初始化TCP和处理数据及定时上传以接口形式抽取出来。

先抽取处理仪器数据接口IMachDealData,具体仪器只需要实现该接口即可,抽取变性很重要

package LIS.Core.Socket;

import java.io.PrintWriter;
import java.net.Socket;

/**
 * 仪器处理数据接口,具体的仪器接口实现此接口后初始化MachSocketBase对象
 */
public interface IMachDealData {
    /**
     * 处理上传定时器接口
     * @param sender Socket对象,用来发送比特用
     * @param writer 用来发布初始化指定的字符用
     */
    public void DealUpTimer(Socket sender, PrintWriter writer);

    /**
     * 处理数据接收
     * @param data 公共层处理成字符串的数据
     * @param buf 没处理的比特数组
     * @param sender Socket对象,用来发送比特用
     * @param writer 用来发布初始化指定的字符用
     */
    public void DealReceive(String data, byte[] buf, Socket sender, PrintWriter writer);

}

然后实现MachSocketBase类作为连接TCP仪器的基础类,里面实现了tcp的客户端和服务端和定时器,里面对数据处理都是调用接口操作

package LIS.Core.Socket;

import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;

import LIS.Core.Socket.IMachDealData;
import LIS.Core.Util.LogUtils;

/**
 * 通过Socket连接的仪器操作基类,由该类提供TCP客户端和服务端的公共逻辑,并且调用处理数据接口处理数据,从而分离TCP主体和业务处理逻辑,达到简化连设备目的
 */
public class MachSocketBase {
    /**
     * 发送数据的操作对象
     */
    public Socket Sender = null;

    /**
     * 写操作对象
     */
    public PrintWriter Writer = null;

    /**
     * 主动上传定时器
     */
    public Timer UpTimer = null;

    /**
     * 处理接口对象
     */
    public IMachDealData DealObj;

    /**
     * 编码
     */
    public String Encode;

    /**
     * 要关闭的客户端端口
     */
    private Socket closeSocket = null;

    /**
     * 要关闭的服务端口
     */
    private ServerSocket closeServerSocket = null;

    /**
     * 存IP和端口的唯一标识串
     */
    public String ID;

    /**
     * 处理类的全面
     */
    public String DealClassName;

    /**
     * 主处理进程
     */
    Thread MainThread = null;

    /**
     * 定时器任务
     */
    TimerTask timerTask = null;

    /**
     * 上传间隔
     */
    Long UpPeriod = null;

    /**
     * 存启动和停止的错误串
     */
    public String Err="";

    /**
     * 停止负载
     *
     * @return 有错误就返回错误
     */
    public String Stop() {
        try {
            if (MainThread != null) {
                MainThread.interrupt();
            }
            if (UpTimer != null) {
                UpTimer.cancel();
            }
            if (closeSocket != null) {
                closeSocket.close();
                ;
            }
            if (closeServerSocket != null) {
                closeServerSocket.close();
                ;
            }
        } catch (Exception ex) {
            Err=ex.getCause().getMessage();
            LogUtils.WriteExceptionLog("停止仪器TCP异常", ex);
            return ex.getCause().getMessage();
        }
        return "";
    }


    /**
     * 启动连接和定时器
     * @return 有错误就返回错误
     */
    public String Start() {
        try {
            if (UpTimer != null) {
                // 设置定时器的执行策略,延迟0秒后开始执行,每隔1秒执行一次
                UpTimer.schedule(timerTask, 0, UpPeriod);
            }
            if (MainThread != null) {
                MainThread.start();
            }
        } catch (Exception ex) {
            Err=ex.getCause().getMessage();
            LogUtils.WriteExceptionLog("启动仪器TCP异常", ex);
            return ex.getCause().getMessage();
        }
        return "";

    }


    /**
     * 构造一个Socket基础并且启动Socket
     *
     * @param ip       IP地址,当服务端传空串
     * @param port     端口
     * @param upPeriod 上传毫秒间隔,不是主动上传的传null
     * @param dealObj  处理接口实现类
     * @param encode   编码格式,传null或空串默认为GBK
     */
    public MachSocketBase(String ip, int port, Long upPeriod, IMachDealData dealObj, String encode) {
        //处理对象
        DealObj = dealObj;
        //编码
        if (encode == null || encode.isEmpty()) {
            encode = "GBK";
        }
        Encode = encode;
        UpPeriod = upPeriod;
        ID = ip + ":" + port;
        if(dealObj!=null)
        {
            DealClassName=dealObj.getClass().getName();
        }
        //上传定时器
        if (upPeriod != null) {
            UpTimer = new Timer();
            timerTask = new TimerTask() {
                @Override
                public void run() {
                    try {
                        //初始化写对象
                        if (Sender != null && Writer == null) {
                            Writer = new PrintWriter(new OutputStreamWriter(Sender.getOutputStream(), Encode), false);
                        }
                        DealObj.DealUpTimer(Sender, Writer);
                    } catch (Exception ex) {
                        LogUtils.WriteExceptionLog("仪器上传定时器异常", ex);
                    }
                }
            };

        }
        //当客户端
        if (!ip.isEmpty()) {
            MainThread = new Thread(new Runnable() {
                @Override
                public void run() {
                    //得到输入流
                    InputStream inputStream = null;
                    //创建Socket对象并连接到服务端
                    Socket socket = null;
                    try {
                        //创建Socket对象并连接到服务端
                        socket = new Socket(ip, port);
                        Sender = socket;
                        closeSocket = socket;
                        Writer = new PrintWriter(new OutputStreamWriter(Sender.getOutputStream(), Encode), false);
                        //得到输入流
                        inputStream = socket.getInputStream();
                        //IO读取
                        byte[] buf = new byte[102400];
                        int readlen = 0;
                        //阻塞读取数据
                        while ((readlen = inputStream.read(buf)) != -1) {
                            String res = new String(buf, 0, readlen, Encode);
                            byte[] targetArray = new byte[readlen];
                            System.arraycopy(buf, 0, targetArray, 0, readlen);
                            //处理接收数据
                            DealObj.DealReceive(res, targetArray, Sender, Writer);
                        }
                    } catch (IOException ex) {
                        LogUtils.WriteExceptionLog("侦听仪器TCP异常", ex);
                    } finally {
                        try {
                            if (inputStream != null) {
                                //关闭输入
                                inputStream.close();
                            }
                            if (Writer != null) {
                                Writer.flush();
                                //关闭输出
                                Writer.close();
                            }
                            if (socket != null) {
                                // 关闭连接
                                socket.close();
                            }
                        } catch (Exception ex) {
                            LogUtils.WriteExceptionLog("释放TCP资源异常", ex);
                        }

                    }
                }
            });

        }
        //当服务端
        else {
            MainThread = new Thread(new Runnable() {
                @Override
                public void run() {
                    //得到输入流
                    InputStream inputStream = null;
                    //创建Socket对象并连接到服务端
                    Socket socket = null;
                    try {
                        ServerSocket serverSocket = new ServerSocket(port);
                        closeServerSocket = serverSocket;
                        //增加一个无限循环
                        while (true) {
                            //等待客户端连接,阻塞
                            socket = serverSocket.accept();
                            Sender = socket;
                            //得到输出流
                            Writer = new PrintWriter(new OutputStreamWriter(Sender.getOutputStream(), Encode), false);
                            //得到输入流
                            inputStream = socket.getInputStream();
                            //IO读取
                            byte[] buf = new byte[102400];
                            int readlen = 0;
                            //阻塞读取数据
                            while ((readlen = inputStream.read(buf)) != -1) {
                                String res = new String(buf, 0, readlen, Encode);
                                byte[] targetArray = new byte[readlen];
                                System.arraycopy(buf, 0, targetArray, 0, readlen);
                                //处理接收数据
                                DealObj.DealReceive(res, targetArray, Sender, Writer);
                            }
                        }
                    } catch (IOException ex) {
                        LogUtils.WriteExceptionLog("侦听仪器TCP异常", ex);
                    } finally {
                        try {
                            if (inputStream != null) {
                                //关闭输入
                                inputStream.close();
                            }
                            if (Writer != null) {
                                Writer.flush();
                                //关闭输出
                                Writer.close();
                            }
                            if (socket != null) {
                                // 关闭连接
                                socket.close();
                            }
                        } catch (Exception ex) {
                            LogUtils.WriteExceptionLog("释放TCP资源异常", ex);
                        }

                    }
                }
            });
        }

    }
}

然后为了统一管理所有TCP仪器提供MachManager,接口开发者之需要关心GetMachSocketBase方法

package LIS.Core.Socket;

import java.util.Iterator;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import LIS.Core.Socket.MachSocketBase;
import LIS.Core.Socket.IMachDealData;

/**
 * 统一管理所有仪器的MachSocketBase对象
 */
public class MachManager {
    /**
     * 存所有仪器连接
     */
    public static ConcurrentHashMap<String, MachSocketBase> AllMach = new ConcurrentHashMap<>();

    /**
     * 更新处理接口对象
     * @param dealClass
     * @throws Exception
     */
    public static void UpdateDeal(Class dealClass) throws Exception
    {
        String dealClsName=dealClass.getName();
        for (Map.Entry<String, MachSocketBase> entry : AllMach.entrySet()) {
            String key = entry.getKey();
            MachSocketBase value = entry.getValue();
            if(value.DealClassName.equals(dealClsName))
            {
                //创建对象
                Object o = dealClass.getConstructor().newInstance();
                value.DealObj=(IMachDealData)o;
                System.out.println("更新:"+value.ID+"的数据处理类");
            }
        }
    }

    /**
     * 得到仪器连接的Socket管理类,直接就启动了控制了
     * @param ip IP地址,当服务端传空串
     * @param port 端口
     * @param upPeriod 上传毫秒间隔,不是主动上传的传null
     * @param dealObj 处理接口实现类
     * @param encode 编码格式,传null或空串默认为GBK
     * @return
     */
    public static MachSocketBase GetMachSocketBase(String ip, int port, Long upPeriod, IMachDealData dealObj, String encode)
    {
        MachSocketBase base=new MachSocketBase(ip,port,upPeriod,dealObj,encode);
        //注册到管理
        RegisterMachSocketBase(base);
        return base;
    }

    /**
     * 注册到管理
     * @param base
     */
    public static void RegisterMachSocketBase(MachSocketBase base)
    {
        //先停止老的接口
        if(AllMach.containsKey(base.ID))
        {
            AllMach.get(base.ID).Stop();
            AllMach.remove(base.ID);
        }
        //加入管理
        AllMach.put(base.ID,base);
        base.Start();
    }
}

然后为了解决修改仪器接口脚本后实时生效实现仪器注解,接口修改后会自动调用MachManager.UpdateDeal更新处理类,这样不用频繁重启仪器控制

//特性,申明此特性的仪器接口在修改后自动调用启动
package LIS.Core.CustomAttributes;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * 申明此特性的仪器接口在修改后自动调用启动
 */
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface  Mach {

}

jbase仪器接口设计_第2张图片

然后实现一个实例仪器接口
jbase仪器接口设计_第3张图片

import LIS.Core.CustomAttributes.Mach;
import LIS.Core.Socket.IMachDealData;
import LIS.Core.Socket.MachSocketBase;
import appcode.BaseHttpHandlerNoSession;
import appcode.Helper;

import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.Socket;

/**
 *连仪器示例代码
 */
@Mach
public class MachDemo extends BaseHttpHandlerNoSession implements IMachDealData {
    /**
     * 缓存数据
     */
    private static StringBuilder dataCache=new StringBuilder();

    //换行符
    private static String lineStr=System.getProperty("line.separator");

    /**
     * 显示日志
     * http://localhost:8080/ankilis/mi/MachDemo.ashx?Method=ShowLog
     * @return
     */
    public String ShowLog()
    {
        return dataCache.toString();
    }

    /**
     * 启动控制
     * http://localhost:8080/ankilis/mi/MachDemo.ashx?Method=Start
     * @return
     * @throws Exception
     */
    public String Start() throws Exception
    {
        //ip地址
        String ip=Helper.ValidParam(LIS.Core.MultiPlatform.LISContext.GetRequest(Request, "ip"), "");
        //端口
        int port=Helper.ValidParam(LIS.Core.MultiPlatform.LISContext.GetRequest(Request, "port"), 8888);
        MachSocketBase base=LIS.Core.Socket.MachManager.GetMachSocketBase(ip,port,Long.valueOf(5000),this,"GBK");
        if(base.Err.isEmpty())
        {
            return Helper.Success();
        }
        else
        {
            return Helper.Error(base.Err);
        }
    }

    /**
     * 处理上传定时器接口
     * @param sender Socket对象,用来发送比特用
     * @param writer 用来发布初始化指定的字符用
     */
    public void DealUpTimer(Socket sender, PrintWriter writer)
    {
        String sendStr="H->M:我主动定时给你推送的数据
"
; //返回数据 writer.print(sendStr); writer.flush(); dataCache.append(sendStr+lineStr); } /** * 处理数据接收 * @param data 公共层处理成字符串的数据 * @param buf 没处理的比特数组 * @param sender Socket对象,用来发送比特用 * @param writer 用来发布初始化指定的字符用 */ public void DealReceive(String data, byte[] buf, Socket sender, PrintWriter writer) { //缓存数据 dataCache.append("M->H:"+data+lineStr); String sendStr="H->M:我收到你发给我的:"+data+"
"
; //返回数据 writer.print(sendStr); writer.flush(); dataCache.append(sendStr+lineStr); } }

启动控制
jbase仪器接口设计_第4张图片

jbase仪器接口设计_第5张图片

jbase仪器接口设计_第6张图片

这样就可以在这个基础上轻松连接TCP的仪器了,还是脚本化、还不用来回重启控制

你可能感兴趣的:(python,开发语言)