From 556c49e32f5f38cca479eb2a12025712b2f1807b Mon Sep 17 00:00:00 2001
From: 18516761980 <4761516tqsxp>
Date: 星期三, 08 六月 2022 17:59:21 +0800
Subject: [PATCH] #

---
 src/main/java/com/zy/core/netty/constant/Constant.java           |   15 
 src/main/java/com/zy/core/netty/domain/ChPackage.java            |   72 +++
 src/main/java/com/zy/core/netty/properties/TcpProperties.java    |   45 +
 src/main/java/com/zy/core/netty/handle/ProtocolEncoder.java      |   51 ++
 src/main/java/com/zy/core/netty/AbstractInboundHandler.java      |   34 +
 src/main/java/com/zy/core/netty/OnlineServer.java                |   92 ++++
 src/main/java/com/zy/core/netty/cache/ChannelCache.java          |   63 ++
 src/main/java/com/zy/core/netty/handle/PackageServerHandler.java |   67 ++
 src/main/java/com/zy/core/netty/handle/ProtocolDecoder.java      |  113 ++++
 src/main/java/com/zy/core/netty/handle/ProtectorHandler.java     |   69 +++
 src/main/java/com/zy/core/netty/cache/ChannelAttrKey.java        |   18 
 src/main/webapp/views/render.js                                  |  624 +++++++++++++++-----------
 src/main/java/com/zy/core/netty/HandlerInitializer.java          |   77 +++
 src/main/java/com/zy/core/thread/SiemensDevpThread.java          |    5 
 14 files changed, 1,079 insertions(+), 266 deletions(-)

diff --git a/src/main/java/com/zy/core/netty/AbstractInboundHandler.java b/src/main/java/com/zy/core/netty/AbstractInboundHandler.java
new file mode 100644
index 0000000..cd377c9
--- /dev/null
+++ b/src/main/java/com/zy/core/netty/AbstractInboundHandler.java
@@ -0,0 +1,34 @@
+package com.zy.core.netty;
+
+import io.netty.buffer.ByteBuf;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.channel.ChannelInboundHandlerAdapter;
+import io.netty.util.ReferenceCountUtil;
+
+/**
+ * netty handler澧炲己鍣�
+ * 璁捐妯″紡: 閫傞厤鍣ㄦā寮�
+ * Created by vincent on 2019-04-02
+ */
+public abstract class AbstractInboundHandler<T> extends ChannelInboundHandlerAdapter {
+
+    @Override
+    public void channelRead(ChannelHandlerContext ctx, Object obj) throws Exception {
+        @SuppressWarnings("unchecked")
+        T t = (T) obj;
+        if (channelRead0(ctx, t)) {
+            ctx.fireChannelRead(t);
+        } else  {
+            // 绠¢亾涓柇锛宖ireChannelRead鏈墽琛岋紝闇�瑕佹墜鍔ㄩ噴鏀惧爢澶栧唴瀛�
+            if (obj instanceof ByteBuf) {
+                ReferenceCountUtil.release(obj);
+            }
+//            if (obj instanceof GBPackage){
+//                GBPackage pac = (GBPackage) obj;
+//                ReferenceCountUtil.release(pac.getSourceBuff());
+//            }
+        }
+    }
+
+    protected abstract boolean channelRead0(ChannelHandlerContext ctx, T t) throws Exception;
+}
diff --git a/src/main/java/com/zy/core/netty/HandlerInitializer.java b/src/main/java/com/zy/core/netty/HandlerInitializer.java
new file mode 100644
index 0000000..0ccfed1
--- /dev/null
+++ b/src/main/java/com/zy/core/netty/HandlerInitializer.java
@@ -0,0 +1,77 @@
+package com.zy.core.netty;
+
+import com.core.common.SnowflakeIdWorker;
+import com.zy.core.netty.cache.ChannelAttrKey;
+import com.zy.core.netty.handle.PackageServerHandler;
+import com.zy.core.netty.handle.ProtectorHandler;
+import com.zy.core.netty.handle.ProtocolDecoder;
+import com.zy.core.netty.handle.ProtocolEncoder;
+import com.zy.core.netty.properties.TcpProperties;
+import io.netty.channel.Channel;
+import io.netty.channel.ChannelHandler;
+import io.netty.channel.ChannelInitializer;
+import io.netty.handler.timeout.IdleStateHandler;
+import io.netty.util.Attribute;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import java.util.HashMap;
+import java.util.Map;
+
+
+/**
+ * handler绠¢亾
+ * 鎺у埗鎵�鏈塶etty handler娴佸悜
+ * 寰呭畬鎴�: 鍔ㄦ�佺鐞唄andler
+ * Created by vincent on 2019-04-02
+ */
+@Component
+@ChannelHandler.Sharable
+public class HandlerInitializer extends ChannelInitializer<Channel> {
+
+    @Autowired
+    private SnowflakeIdWorker snowflakeIdWorker;
+    @Autowired
+    private TcpProperties tcpProperties;
+    @Autowired
+    private ProtocolEncoder protocolEncoder;
+    @Autowired
+    private PackageServerHandler packageServerHandler;
+    @Autowired
+    private ProtectorHandler protectorHandler;
+
+    /**
+     * Set some channel handlers on channel pipeline
+     */
+    @Override
+    protected void initChannel(Channel channel) {
+        channel.pipeline()
+                // 蹇冭烦
+                .addLast(new IdleStateHandler(tcpProperties.getHeartSeconds(), 0, 0))
+                // 缂栫爜鍣�
+                .addLast(protocolEncoder)
+                // 瑙g爜鍣�
+                .addLast(new ProtocolDecoder(snowflakeIdWorker))
+                // 鏍¢獙鐮佸鐞嗗櫒
+//                .addLast(validateHandler)
+                // 璁よ瘉澶勭悊鍣�
+//                .addLast(vehAuthHandler)
+                // 涓氬姟澶勭悊鍣�
+                .addLast(packageServerHandler)
+                // 閫氶亾淇濇姢鍣�
+                .addLast(protectorHandler);
+
+        // Channel灞�閮ㄥ彉閲忥紝鐩稿綋浜庣嚎绋嬬殑ThreadLocal
+//        initAttrTrack(channel);
+    }
+
+    /**
+     *  Init channel attr track
+     */
+    private void initAttrTrack(Channel channel){
+        Attribute<Map<String, Object>> coolTrackAttr = channel.attr(ChannelAttrKey.DATA_MAP_ATTR);
+        Map<String, Object> trackMap = new HashMap<>();
+        coolTrackAttr.setIfAbsent(trackMap);
+    }
+
+}
diff --git a/src/main/java/com/zy/core/netty/OnlineServer.java b/src/main/java/com/zy/core/netty/OnlineServer.java
new file mode 100644
index 0000000..f6e68ec
--- /dev/null
+++ b/src/main/java/com/zy/core/netty/OnlineServer.java
@@ -0,0 +1,92 @@
+package com.zy.core.netty;
+
+
+import com.zy.core.netty.properties.TcpProperties;
+import io.netty.bootstrap.ServerBootstrap;
+import io.netty.buffer.PooledByteBufAllocator;
+import io.netty.channel.Channel;
+import io.netty.channel.ChannelOption;
+import io.netty.channel.EventLoopGroup;
+import io.netty.channel.nio.NioEventLoopGroup;
+import io.netty.channel.socket.nio.NioServerSocketChannel;
+import io.netty.util.ResourceLeakDetector;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import javax.annotation.PostConstruct;
+import javax.annotation.PreDestroy;
+
+/**
+ * TCP/IP鍗忚绔彛
+ * Netty Server 寮曞绫�
+ * @author Vincent
+ */
+@Slf4j
+@Component
+public class OnlineServer {
+
+    private final HandlerInitializer handlerInitializer;
+    private final TcpProperties tcpProperties;
+    private Channel channel;
+    private ServerBootstrap bootstrap;
+    private EventLoopGroup bossGroup;
+    private EventLoopGroup workerGroup;
+
+    @Autowired
+    public OnlineServer(TcpProperties tcpProperties, HandlerInitializer handlerInitializer) { ;
+        this.tcpProperties = tcpProperties;
+        this.handlerInitializer = handlerInitializer;
+    }
+
+    {
+        bootstrap = new ServerBootstrap();
+        bossGroup = new NioEventLoopGroup(1);
+        workerGroup = new NioEventLoopGroup();
+    }
+
+
+    /**
+     *  tcp server init
+     */
+    @PostConstruct
+    public void serverStart() throws Exception {
+        // 寮�鍚ぇ绔ā寮�
+//        CStruct.reverse = false;
+
+        bootstrap.group(bossGroup, workerGroup)
+                .channel(NioServerSocketChannel.class)
+                .childHandler(handlerInitializer)
+                .option(ChannelOption.SO_BACKLOG, tcpProperties.getBacklog())
+                .option(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT)
+                .childOption(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT)
+                .childOption(ChannelOption.SO_KEEPALIVE, tcpProperties.isKeepAlive())
+                .childOption(ChannelOption.SO_SNDBUF, tcpProperties.getSndbuf())
+                .childOption(ChannelOption.SO_RCVBUF, tcpProperties.getRcvbuf());
+
+        ResourceLeakDetector.setLevel(ResourceLeakDetector.Level.ADVANCED);
+
+        log.info("TCP server started successfully, port锛歿}", tcpProperties.getPort());
+
+        channel = bootstrap.bind(tcpProperties.getPort()).sync().channel();
+    }
+
+
+    /**
+     * tcp server stop
+     */
+    @PreDestroy
+    public void destroy() {
+        if (channel != null && channel.isActive()) {
+            channel.close();
+        }
+        if (bossGroup != null) {
+            bossGroup.shutdownGracefully();
+        }
+        if (workerGroup != null) {
+            workerGroup.shutdownGracefully();
+        }
+        log.info("TCP server stopped successfully, port: {}", tcpProperties.getPort());
+    }
+
+}
diff --git a/src/main/java/com/zy/core/netty/cache/ChannelAttrKey.java b/src/main/java/com/zy/core/netty/cache/ChannelAttrKey.java
new file mode 100644
index 0000000..ee15ba5
--- /dev/null
+++ b/src/main/java/com/zy/core/netty/cache/ChannelAttrKey.java
@@ -0,0 +1,18 @@
+package com.zy.core.netty.cache;
+
+
+import io.netty.util.AttributeKey;
+
+import java.util.Map;
+
+/**
+ * Channel灞�閮ㄥ彉閲忕紦瀛� ==>> 绾跨▼瀹夊叏
+ * Created by vincent on 2019-04-02
+ */
+public final class ChannelAttrKey {
+
+    private static String CHANNEL_ATTR_KEY_VC_TRACK = "channel.attr.vc.track";
+
+    public static AttributeKey<Map<String, Object>> DATA_MAP_ATTR = AttributeKey.newInstance(CHANNEL_ATTR_KEY_VC_TRACK);
+
+}
diff --git a/src/main/java/com/zy/core/netty/cache/ChannelCache.java b/src/main/java/com/zy/core/netty/cache/ChannelCache.java
new file mode 100644
index 0000000..f122ba6
--- /dev/null
+++ b/src/main/java/com/zy/core/netty/cache/ChannelCache.java
@@ -0,0 +1,63 @@
+package com.zy.core.netty.cache;
+
+import io.netty.channel.Channel;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * Channel缂撳瓨 ==>> {
+ *                   key: uuid
+ *                   value: Channel
+ *                 }
+ * Created by vincent on 2019-04-02
+ */
+@Slf4j
+@RestController
+public class ChannelCache {
+
+    private static Map<String, Channel> channelGroup = new ConcurrentHashMap<>();
+
+    public static void setChannel(String uuid, Channel channel){
+        // todo 缂撳瓨鏍囪 ===>> 鍒嗗竷寮忕郴缁�
+//        String hostName = SystemProperties.HOST_NAME;
+        if (getChannel(uuid) == channel){
+            return;
+        }
+        removeChannel(uuid);
+        channelGroup.put(uuid, channel);
+    }
+
+    public static Channel getChannel(String uuid){
+        return channelGroup.get(uuid);
+    }
+
+    public static void removeChannel(String uuid) {
+        Channel channel = getChannel(uuid);
+        if (null == channel){
+            return;
+        }
+        channelGroup.remove(uuid);
+        channel.close();
+    }
+
+    public static String removeChannel(Channel channel){
+        String key = null;
+        for (Map.Entry<String, Channel> entry : channelGroup.entrySet()){
+            if (entry.getValue() == channel){
+                key = entry.getKey();
+                break;
+            }
+        }
+        if (null != key){
+            channelGroup.remove(key);
+            return key;
+        }
+        return null;
+    }
+
+
+
+}
diff --git a/src/main/java/com/zy/core/netty/constant/Constant.java b/src/main/java/com/zy/core/netty/constant/Constant.java
new file mode 100644
index 0000000..d228f38
--- /dev/null
+++ b/src/main/java/com/zy/core/netty/constant/Constant.java
@@ -0,0 +1,15 @@
+package com.zy.core.netty.constant;
+
+import java.nio.charset.Charset;
+
+/**
+ * 閰嶇疆甯搁噺
+ * Created by vincent on 2019-04-03
+ */
+public class Constant {
+
+    public static final String SYMBOL = "##";
+
+    public static final Charset CHARSET_GBK = Charset.forName("GBK");
+
+}
diff --git a/src/main/java/com/zy/core/netty/domain/ChPackage.java b/src/main/java/com/zy/core/netty/domain/ChPackage.java
new file mode 100644
index 0000000..555a8c2
--- /dev/null
+++ b/src/main/java/com/zy/core/netty/domain/ChPackage.java
@@ -0,0 +1,72 @@
+package com.zy.core.netty.domain;
+
+import io.netty.buffer.ByteBuf;
+import lombok.Data;
+
+/**
+ * 鎶ユ枃妯″瀷
+ * Created by vincent on 2019-04-03
+ */
+@Data
+public class ChPackage {
+
+    /**
+     * 鍞竴缂栫爜
+     */
+    private String uuid;
+
+    /**
+     * 婧愭暟鎹寘缂撳啿鍖�(寮曠敤)
+     */
+    private ByteBuf sourceBuff;
+
+    /**
+     * 鍘熷娑堟伅瀵瑰簲鐨�16杩涘埗瀛楃涓�
+     */
+    private String sourceHexStr;
+
+    /**
+     * 璇锋眰浣�
+     */
+    private ByteBuf content;
+
+    private byte[] bytes;
+
+    private String ascii;
+
+    private String ip;
+
+    /**
+     * 娑堟伅鐨勬牎姝g爜
+     */
+    private byte validCode;
+
+    /**
+     * 鏄惁涓烘牎楠屽紓甯稿寘
+     */
+    private boolean errorPac;
+
+    public static ChPackage valueOfEmpty(String uuid, String ip) {
+        ChPackage chPackage = new ChPackage();
+        chPackage.setUuid(uuid);
+        chPackage.setIp(ip);
+        return chPackage;
+    }
+
+//    public ByteBuf convert(ByteBuf byteBuf){
+//        byteBuf.writeBytes(this.getHeader().getStartSymbol().getBytes(Constant.CHARSET_GBK))
+//                .writeByte(this.getHeader().getCommandMark().getCode())
+//                .writeByte(this.getHeader().getAckMark().getCode())
+//                .writeBytes(this.getHeader().getUniqueNo().getBytes())
+//                .writeByte(this.getHeader().getEncryptType().getCode())
+//                .writeShort(this.getHeader().getContentLength())
+//                .writeBytes(this.getBody().getContent())
+//                .writeByte(this.getValidCode());
+//        // 璁$畻骞惰缃牎楠岀爜
+//        this.setValidCode(ValidUtil.caculateValidByteFromBuff(byteBuf));
+//        byteBuf.resetReaderIndex();
+//        byteBuf.writerIndex(byteBuf.readableBytes() - 1).writeByte(this.getValidCode());
+//        return byteBuf;
+//    }
+
+}
diff --git a/src/main/java/com/zy/core/netty/handle/PackageServerHandler.java b/src/main/java/com/zy/core/netty/handle/PackageServerHandler.java
new file mode 100644
index 0000000..fc7f7a5
--- /dev/null
+++ b/src/main/java/com/zy/core/netty/handle/PackageServerHandler.java
@@ -0,0 +1,67 @@
+package com.zy.core.netty.handle;
+
+import com.core.common.Cools;
+import com.zy.core.Slave;
+import com.zy.core.cache.SlaveConnection;
+import com.zy.core.enums.SlaveType;
+import com.zy.core.netty.AbstractInboundHandler;
+import com.zy.core.netty.cache.ChannelCache;
+import com.zy.core.netty.domain.ChPackage;
+import com.zy.core.netty.properties.TcpProperties;
+import com.zy.core.properties.SlaveProperties;
+import com.zy.core.thread.BarcodeThread;
+import io.netty.channel.Channel;
+import io.netty.channel.ChannelHandler;
+import io.netty.channel.ChannelHandlerContext;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+/**
+ * 鍥芥爣涓氬姟澶勭悊handler
+ * Created by vincent on 2019-04-02
+ */
+@Slf4j
+@Component
+@ChannelHandler.Sharable
+public class PackageServerHandler extends AbstractInboundHandler<ChPackage> {
+
+    @Autowired
+    private SlaveProperties slaveProperties;
+    @Autowired
+    private TcpProperties tcpProperties;
+
+    @Override
+    protected boolean channelRead0(ChannelHandlerContext ctx, ChPackage pac) {
+//        log.info("璇荤爜鍣ㄣ�怚P:{}銆� 涓婅鏁版嵁 ===>> {}", pac.getIp(), pac.getAscii());
+//        //鎵爜涓婁紶鏁版嵁鏍煎紡蹇呴』2涓�#寮�澶达紝濡�:##12345678
+//        String msg = pac.getAscii().replaceAll("#", "");
+//        if(!Cools.isEmpty(msg) && msg.length()>=tcpProperties.getBarcodeLen()){
+//            msg = msg.substring(0,tcpProperties.getBarcodeLen());
+//
+//            for (Slave slave : slaveProperties.getBarcode()) {
+//                if (slave.getIp().equals(pac.getIp())) {
+//                    BarcodeThread barcodeThread = (BarcodeThread) SlaveConnection.get(SlaveType.Barcode, slave.getId());
+//                    if (barcodeThread == null) { continue; }
+//                    barcodeThread.setBarcode(msg);
+//                    break;
+//                }
+//            }
+//        }
+
+        return true;
+    }
+
+    /**
+     * 鏁版嵁涓嬭
+     */
+    public static void write(ChPackage chPackage){
+        String uuid = chPackage.getUuid();
+        Channel channel = ChannelCache.getChannel(uuid);
+        if (null == channel){
+            log.warn("閫氶亾uuid={} 涓嶅湪绾�", uuid);
+            return;
+        }
+        channel.writeAndFlush(chPackage);
+    }
+}
diff --git a/src/main/java/com/zy/core/netty/handle/ProtectorHandler.java b/src/main/java/com/zy/core/netty/handle/ProtectorHandler.java
new file mode 100644
index 0000000..ec0b43c
--- /dev/null
+++ b/src/main/java/com/zy/core/netty/handle/ProtectorHandler.java
@@ -0,0 +1,69 @@
+package com.zy.core.netty.handle;
+
+import com.core.common.Cools;
+import com.zy.core.netty.AbstractInboundHandler;
+import com.zy.core.netty.cache.ChannelCache;
+import com.zy.core.netty.domain.ChPackage;
+import io.netty.channel.ChannelHandler;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.handler.timeout.IdleState;
+import io.netty.handler.timeout.IdleStateEvent;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Component;
+
+/**
+ * 閫氶亾缁存姢handler
+ * Created by vincent on 2019-04-11
+ */
+@Slf4j
+@Component("protectorHandler")
+@ChannelHandler.Sharable
+public class ProtectorHandler extends AbstractInboundHandler<ChPackage> {
+
+    @Override
+    protected boolean channelRead0(ChannelHandlerContext ctx, ChPackage pac) throws Exception {
+        // jvm鍫嗗鍐呭瓨闇�瑕佹墜鍔ㄩ噴鏀�
+//        ReferenceCountUtil.release(pac.getSourceBuff());
+        return true;
+    }
+
+    /**
+     * 绌洪棽鍓旈櫎
+     */
+    @Override
+    public void userEventTriggered(ChannelHandlerContext ctx, Object evt) {
+        if (evt instanceof IdleStateEvent) {
+            IdleStateEvent e = (IdleStateEvent) evt;
+            if (IdleState.READER_IDLE == e.state()) {
+                String uuid = ChannelCache.removeChannel(ctx.channel());
+                ctx.close();
+                if (!Cools.isEmpty(uuid)){
+                    log.info("uuid={} 绌洪棽鍓旈櫎", uuid);
+                }
+            }
+        }
+    }
+
+    /**
+     * 鏂紑杩炴帴
+     */
+    @Override
+    public void channelInactive(ChannelHandlerContext ctx) {
+        String uuid = ChannelCache.removeChannel(ctx.channel());
+        ctx.close();
+        if (!Cools.isEmpty(uuid)){
+            log.info("閫氶亾 uuid={} 澶卞幓杩炴帴", uuid);
+        }
+    }
+
+    /**
+     * 绠¢亾寮傚父
+     */
+    @Override
+    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
+        cause.printStackTrace();
+        ChannelCache.removeChannel(ctx.channel());
+        ctx.close();
+    }
+
+}
diff --git a/src/main/java/com/zy/core/netty/handle/ProtocolDecoder.java b/src/main/java/com/zy/core/netty/handle/ProtocolDecoder.java
new file mode 100644
index 0000000..7cb12d7
--- /dev/null
+++ b/src/main/java/com/zy/core/netty/handle/ProtocolDecoder.java
@@ -0,0 +1,113 @@
+package com.zy.core.netty.handle;
+
+import com.core.common.SnowflakeIdWorker;
+import com.zy.core.netty.constant.Constant;
+import com.zy.core.netty.domain.ChPackage;
+import io.netty.buffer.ByteBuf;
+import io.netty.buffer.ByteBufUtil;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.handler.codec.ByteToMessageDecoder;
+
+import java.net.InetSocketAddress;
+import java.nio.charset.StandardCharsets;
+import java.util.List;
+
+/**
+ * Created by vincent on 2019-04-10
+ */
+public class ProtocolDecoder extends ByteToMessageDecoder {
+
+    private final SnowflakeIdWorker snowflakeIdWorker;
+
+    public ProtocolDecoder(SnowflakeIdWorker snowflakeIdWorker){
+        this.snowflakeIdWorker = snowflakeIdWorker;
+    }
+
+
+    @Override
+    protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> list) throws Exception {
+        int startMark = indexOfStartMark(in);
+        if (startMark == -1){
+            return;
+        }
+        // 鍘婚櫎鏃犵敤鍓嶇紑鎶ユ枃
+        if (startMark != 0){
+            in.readerIndex(startMark);
+            in.discardReadBytes();
+        }
+        // 鐢熸垚鍜屽垵濮嬪寲娑堟伅鍖呰绫�
+
+        String ip = ((InetSocketAddress) ctx.channel().remoteAddress()).getAddress().getHostAddress();
+        ChPackage pac = ChPackage.valueOfEmpty(String.valueOf(snowflakeIdWorker.nextId()), ip);
+
+        pac.setSourceBuff(in);
+
+        // 瑙f瀽
+        list.add(analyzeProtocol(pac));
+    }
+
+    public ChPackage analyzeProtocol(ChPackage pac){
+
+        ByteBuf byteBuf = pac.getSourceBuff();
+
+        // 澶囦唤缂撳啿鍖�
+        ByteBuf body = byteBuf.duplicate();
+        if (null != body && body.readableBytes() >= 0) {
+            pac.setContent(body);
+        } else {
+            return null;
+        }
+
+        // 瀛楄妭鏁扮粍
+        body.resetReaderIndex();
+        byte[] bytes = new byte[body.readableBytes()];
+        body.readBytes(bytes);
+        body.resetReaderIndex();
+        pac.setBytes(bytes);
+
+        // ascii
+        if (bytes.length > 0) {
+            pac.setAscii(new String(pac.getBytes(), StandardCharsets.US_ASCII));
+        }
+
+        // 澶囦唤瀛楃涓�
+        if (null != pac.getSourceBuff() && null == pac.getSourceHexStr()) {
+            pac.getSourceBuff().resetReaderIndex();
+            pac.setSourceHexStr(ByteBufUtil.hexDump(pac.getSourceBuff()));
+            pac.getSourceBuff().resetReaderIndex();
+        }
+
+        byteBuf.skipBytes(byteBuf.readableBytes());
+//        pac.getSourceBuff().readByte();
+
+        return pac;
+    }
+
+    // 鑾峰彇鏍囪瘑浣嶄笅鏍�
+    private int indexOfStartMark(ByteBuf inputBuffer){
+        int length = inputBuffer.writerIndex();
+        // 鎶ユ枃闀垮害鑷冲皯澶т簬2
+        if (length < 2) {
+            return -1;
+        }
+        int readerIndex = inputBuffer.readerIndex();
+        for(int i = readerIndex; i < length - 1; i ++) {
+            byte b1 = inputBuffer.getByte(i);
+            // "#" = b1
+            if (0x23 == b1) {
+                // "#" = b2
+                if (i + 1 <= length && 0x23 == inputBuffer.getByte(i + 1)) {
+                    return i;
+                }
+            }
+        }
+        return -1;
+    }
+
+    private String byte2Str(ByteBuf buf, int len) {
+        byte[] bytes = new byte[len];
+        buf.readBytes(bytes);
+        return new String(bytes, Constant.CHARSET_GBK);
+    }
+
+}
diff --git a/src/main/java/com/zy/core/netty/handle/ProtocolEncoder.java b/src/main/java/com/zy/core/netty/handle/ProtocolEncoder.java
new file mode 100644
index 0000000..4da18a9
--- /dev/null
+++ b/src/main/java/com/zy/core/netty/handle/ProtocolEncoder.java
@@ -0,0 +1,51 @@
+package com.zy.core.netty.handle;
+
+import com.zy.core.netty.domain.ChPackage;
+import com.zy.core.netty.properties.TcpProperties;
+import io.netty.buffer.ByteBuf;
+import io.netty.channel.ChannelHandler;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.handler.codec.MessageToByteEncoder;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+/**
+ * 鍥芥爣缂栫爜鍣�
+ * 姝よВ鐮佸櫒灏嗕細鐢熸垚鏍¢獙鐮�
+ * 澶勭悊鏂瑰紡: 寮傛垨鍜�
+ * Created by vincent on 2019-04-02
+ */
+@Slf4j
+@Component
+@ChannelHandler.Sharable
+public class ProtocolEncoder extends MessageToByteEncoder<Object> {
+
+    @Autowired
+    private TcpProperties tcpProperties;
+
+    @Override
+    protected void encode(ChannelHandlerContext ctx, Object obj, ByteBuf out) {
+        boolean upgradeLog = true;
+        if (obj instanceof ByteBuf){
+            out.writeBytes((ByteBuf) obj);
+        } else if (obj instanceof byte[]) {
+            out.writeBytes((byte[]) obj);
+        } else if (obj instanceof ChPackage) {
+
+        }
+//        if (tcpProperties.isPrintPacLog() || upgradeLog){
+//            log.info("uuid={} 涓嬭 >>> {}", getVin(out), ByteBufUtil.hexDump(out));
+//        }
+
+    }
+
+//    private String getVin(ByteBuf byteBuf){
+//        byte[] bytes = new byte[PackagePart.UNIQUENO.getLen()];
+//        byteBuf.markReaderIndex();
+//        byteBuf.readerIndex(PackagePart.UNIQUENO.getStartIndex());
+//        byteBuf.readBytes(bytes);
+//        byteBuf.resetReaderIndex();
+//        return RadixTools.bytesToStr(bytes);
+//    }
+}
diff --git a/src/main/java/com/zy/core/netty/properties/TcpProperties.java b/src/main/java/com/zy/core/netty/properties/TcpProperties.java
new file mode 100644
index 0000000..2907832
--- /dev/null
+++ b/src/main/java/com/zy/core/netty/properties/TcpProperties.java
@@ -0,0 +1,45 @@
+package com.zy.core.netty.properties;
+
+import lombok.Data;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.context.annotation.Configuration;
+
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+
+/**
+ * vc绯荤粺閰嶇疆
+ * Created by luxiaotao on 2018/10/15
+ */
+@Data
+@Configuration
+@ConfigurationProperties(prefix = "tcp")
+public class TcpProperties {
+
+    public static String HOST_NAME;
+
+    static {
+        try {
+            HOST_NAME = InetAddress.getLocalHost().getHostName();
+        } catch (UnknownHostException e) {
+            System.err.println("find hostname err");
+        }
+    }
+
+    private int port;
+
+    private int heartSeconds;
+
+    private int backlog;
+
+    private boolean keepAlive;
+
+    private int sndbuf;
+
+    private int rcvbuf;
+
+    private boolean printPacLog;
+
+    private int barcodeLen;
+
+}
diff --git a/src/main/java/com/zy/core/thread/SiemensDevpThread.java b/src/main/java/com/zy/core/thread/SiemensDevpThread.java
index 6fa259a..66e80c6 100644
--- a/src/main/java/com/zy/core/thread/SiemensDevpThread.java
+++ b/src/main/java/com/zy/core/thread/SiemensDevpThread.java
@@ -53,6 +53,10 @@
         add(120);add(121);add(122);add(123);add(124);add(125);add(126);add(127);add(128);add(129);
         add(130);add(131);add(132);add(133);add(134);add(135);add(136);
     }};
+
+    @Autowired
+    private SlaveProperties slaveProperties;
+
     /**
      * 鏉$爜鏁伴噺
      */
@@ -174,6 +178,7 @@
         }
 
         Thread.sleep(200);
+        barcodeSize = slaveProperties.getBarcode().size();
         OperateResultExOne<byte[]> result2 = siemensS7Net.Read("DB100.190",(short)(barcodeSize*8));
         if (result2.IsSuccess) {
             for (int i = 0; i < barcodeSize; i++) {
diff --git a/src/main/webapp/views/render.js b/src/main/webapp/views/render.js
index e59bfc5..c09c091 100644
--- a/src/main/webapp/views/render.js
+++ b/src/main/webapp/views/render.js
@@ -1,462 +1,554 @@
 data = {
-    "mapName": "HYLYWCS",
-    "rackCount": 16,
-    "crnCount": 4,
-    "stbCount": 14,
-    "hpPosition": 0,
+    "mapName": "KLSWCS",
+    "rackCount": 12,
+    "crnCount": 3,
+    "stbCount": 36,
+    "hpPosition": 1,
     "minBayNo": 2,
     "floors": 1,
     "racks": [{
         "type": "rack",
-        "id": "rack16",
-        "top": 650,
-        "left": 412,
-        "width": 1046,
-        "height": 24,
-        "minBayNo": 2,
-        "maxBayNo": 24
-    }, {
-        "type": "rack",
-        "id": "rack15",
-        "top": 624,
-        "left": 412,
-        "width": 1046,
-        "height": 24,
-        "minBayNo": 2,
-        "maxBayNo": 24
-    }, {
-        "type": "rack",
-        "id": "rack14",
-        "top": 569,
-        "left": 412,
-        "width": 1046,
-        "height": 24,
-        "minBayNo": 2,
-        "maxBayNo": 24
-    }, {
-        "type": "rack",
-        "id": "rack13",
-        "top": 543,
-        "left": 412,
-        "width": 1046,
-        "height": 24,
-        "minBayNo": 2,
-        "maxBayNo": 24
-    }, {
-        "type": "rack",
         "id": "rack12",
-        "top": 484,
-        "left": 412,
-        "width": 1099,
-        "height": 24,
+        "top": 646,
+        "left": 324,
+        "width": 887,
+        "height": 30,
         "minBayNo": 2,
-        "maxBayNo": 64
+        "maxBayNo": 52
     }, {
         "type": "rack",
         "id": "rack11",
-        "top": 458,
-        "left": 412,
-        "width": 1099,
-        "height": 24,
+        "top": 613,
+        "left": 324,
+        "width": 887,
+        "height": 30,
         "minBayNo": 2,
-        "maxBayNo": 64
+        "maxBayNo": 52
     }, {
         "type": "rack",
         "id": "rack10",
-        "top": 399,
-        "left": 412,
-        "width": 1099,
-        "height": 24,
+        "top": 516,
+        "left": 324,
+        "width": 887,
+        "height": 30,
         "minBayNo": 2,
-        "maxBayNo": 64
+        "maxBayNo": 52
     }, {
         "type": "rack",
         "id": "rack9",
-        "top": 373,
-        "left": 412,
-        "width": 1099,
-        "height": 24,
+        "top": 483,
+        "left": 324,
+        "width": 887,
+        "height": 30,
         "minBayNo": 2,
-        "maxBayNo": 64
+        "maxBayNo": 52
     }, {
         "type": "rack",
         "id": "rack8",
-        "top": 344,
-        "left": 405,
-        "width": 979,
-        "height": 24,
+        "top": 451,
+        "left": 324,
+        "width": 887,
+        "height": 30,
         "minBayNo": 2,
-        "maxBayNo": 16
+        "maxBayNo": 52
     }, {
         "type": "rack",
         "id": "rack7",
-        "top": 318,
-        "left": 405,
-        "width": 979,
-        "height": 24,
+        "top": 418,
+        "left": 324,
+        "width": 887,
+        "height": 30,
         "minBayNo": 2,
-        "maxBayNo": 16
+        "maxBayNo": 52
     }, {
         "type": "rack",
         "id": "rack6",
-        "top": 263,
-        "left": 405,
-        "width": 979,
-        "height": 24,
+        "top": 323,
+        "left": 324,
+        "width": 887,
+        "height": 30,
         "minBayNo": 2,
-        "maxBayNo": 16
+        "maxBayNo": 52
     }, {
         "type": "rack",
         "id": "rack5",
-        "top": 237,
-        "left": 405,
-        "width": 979,
-        "height": 24,
+        "top": 290,
+        "left": 324,
+        "width": 887,
+        "height": 30,
         "minBayNo": 2,
-        "maxBayNo": 16
+        "maxBayNo": 52
     }, {
         "type": "rack",
         "id": "rack3",
-        "top": 180,
-        "left": 357,
-        "width": 1027,
-        "height": 24,
+        "top": 225,
+        "left": 188,
+        "width": 1023,
+        "height": 30,
         "minBayNo": 2,
-        "maxBayNo": 31
+        "maxBayNo": 60
     }, {
         "type": "rack",
         "id": "rack1",
         "top": 97,
-        "left": 357,
-        "width": 1027,
-        "height": 24,
+        "left": 188,
+        "width": 1023,
+        "height": 30,
         "minBayNo": 2,
-        "maxBayNo": 31
+        "maxBayNo": 60
     }, {
         "type": "rack",
         "id": "rack4",
-        "top": 207,
-        "left": 357,
-        "width": 1027,
-        "height": 24,
+        "top": 257,
+        "left": 188,
+        "width": 1023,
+        "height": 30,
         "minBayNo": 2,
-        "maxBayNo": 31
+        "maxBayNo": 60
     }, {
         "type": "rack",
         "id": "rack2",
-        "top": 123,
-        "left": 357,
-        "width": 1027,
-        "height": 24,
+        "top": 129,
+        "left": 188,
+        "width": 1023,
+        "height": 30,
         "minBayNo": 2,
-        "maxBayNo": 31
+        "maxBayNo": 60
     }],
     "rackDescs": [{
         "type": "rackDescs",
-        "id": "lb_desc16",
-        "text": "#16",
-        "top": 651,
-        "left": 1471,
-        "width": 41,
-        "height": 23
-    }, {
-        "type": "rackDescs",
-        "id": "lb_desc15",
-        "text": "#15",
-        "top": 626,
-        "left": 1472,
-        "width": 40,
-        "height": 23
-    }, {
-        "type": "rackDescs",
-        "id": "lb_desc14",
-        "text": "#14",
-        "top": 565,
-        "left": 1469,
-        "width": 41,
-        "height": 23
-    }, {
-        "type": "rackDescs",
-        "id": "lb_desc13",
-        "text": "#13",
-        "top": 541,
-        "left": 1472,
-        "width": 40,
-        "height": 23
-    }, {
-        "type": "rackDescs",
         "id": "lb_desc12",
         "text": "#12",
-        "top": 485,
-        "left": 1517,
-        "width": 41,
-        "height": 23
+        "top": 646,
+        "left": 260,
+        "width": 47,
+        "height": 27
     }, {
         "type": "rackDescs",
         "id": "lb_desc11",
         "text": "#11",
-        "top": 460,
-        "left": 1518,
-        "width": 38,
-        "height": 23
+        "top": 618,
+        "left": 260,
+        "width": 44,
+        "height": 27
     }, {
         "type": "rackDescs",
         "id": "lb_desc10",
         "text": "#10",
-        "top": 399,
-        "left": 1515,
-        "width": 42,
-        "height": 23
+        "top": 516,
+        "left": 257,
+        "width": 49,
+        "height": 27
     }, {
         "type": "rackDescs",
         "id": "lb_desc9",
         "text": "#9",
-        "top": 375,
-        "left": 1518,
-        "width": 33,
-        "height": 23
+        "top": 488,
+        "left": 257,
+        "width": 38,
+        "height": 27
     }, {
         "type": "rackDescs",
         "id": "lb_desc8",
         "text": "#8",
-        "top": 349,
-        "left": 1389,
-        "width": 33,
-        "height": 23
+        "top": 449,
+        "left": 260,
+        "width": 39,
+        "height": 27
     }, {
         "type": "rackDescs",
         "id": "lb_desc7",
         "text": "#7",
-        "top": 321,
-        "left": 1389,
-        "width": 32,
-        "height": 23
+        "top": 421,
+        "left": 260,
+        "width": 37,
+        "height": 27
     }, {
         "type": "rackDescs",
         "id": "lb_desc6",
         "text": "#6",
-        "top": 264,
-        "left": 1387,
-        "width": 33,
-        "height": 23
+        "top": 327,
+        "left": 260,
+        "width": 38,
+        "height": 27
     }, {
         "type": "rackDescs",
         "id": "lb_desc5",
         "text": "#5",
-        "top": 235,
-        "left": 1388,
-        "width": 32,
-        "height": 23
+        "top": 291,
+        "left": 261,
+        "width": 38,
+        "height": 27
     }, {
         "type": "rackDescs",
         "id": "lb_desc4",
         "text": "#4",
-        "top": 206,
-        "left": 1388,
-        "width": 33,
-        "height": 23
+        "top": 253,
+        "left": 145,
+        "width": 38,
+        "height": 27
     }, {
         "type": "rackDescs",
         "id": "lb_desc3",
         "text": "#3",
-        "top": 179,
-        "left": 1389,
-        "width": 32,
-        "height": 23
+        "top": 225,
+        "left": 145,
+        "width": 38,
+        "height": 27
     }, {
         "type": "rackDescs",
         "id": "lb_desc2",
         "text": "#2",
-        "top": 123,
-        "left": 1389,
-        "width": 33,
-        "height": 23
+        "top": 128,
+        "left": 145,
+        "width": 38,
+        "height": 27
     }, {
         "type": "rackDescs",
         "id": "lb_desc1",
         "text": "#1",
-        "top": 95,
-        "left": 1389,
-        "width": 30,
-        "height": 23
+        "top": 100,
+        "left": 145,
+        "width": 35,
+        "height": 27
     }],
     "crns": [{
         "type": "crane",
-        "id": "crn-4",
-        "text": "4",
-        "top": 597,
-        "left": 500,
-        "width": 93,
-        "height": 22
-    }, {
-        "type": "track",
-        "id": "lb_track4",
-        "text": "",
-        "top": 606,
-        "left": 366,
-        "width": 1150,
-        "height": 2
-    }, {
-        "type": "crane",
         "id": "crn-1",
         "text": "1",
-        "top": 153,
-        "left": 500,
+        "top": 184,
+        "left": 777,
         "width": 93,
         "height": 22
     }, {
         "type": "crane",
         "id": "crn-2",
         "text": "2",
-        "top": 291,
-        "left": 500,
+        "top": 378,
+        "left": 777,
         "width": 93,
         "height": 22
     }, {
         "type": "crane",
         "id": "crn-3",
         "text": "3",
-        "top": 428,
-        "left": 500,
+        "top": 572,
+        "left": 777,
         "width": 93,
         "height": 22
     }, {
         "type": "track",
         "id": "lb_track2",
         "text": "",
-        "top": 300,
-        "left": 359,
-        "width": 1066,
+        "top": 387,
+        "left": 146,
+        "width": 1112,
         "height": 2
     }, {
         "type": "track",
         "id": "lb_track3",
         "text": "",
-        "top": 438,
-        "left": 359,
-        "width": 1200,
+        "top": 582,
+        "left": 146,
+        "width": 1112,
         "height": 2
     }, {
         "type": "track",
         "id": "lb_track1",
         "text": "",
-        "top": 164,
-        "left": 315,
+        "top": 195,
+        "left": 146,
         "width": 1112,
         "height": 2
     }],
     "stns": [{
         "type": "stn",
+        "id": "site-132",
+        "text": "132",
+        "top": 547,
+        "left": 1399,
+        "width": 60,
+        "height": 63
+    }, {
+        "type": "stn",
+        "id": "site-126",
+        "text": "126",
+        "top": 450,
+        "left": 1461,
+        "width": 60,
+        "height": 63
+    }, {
+        "type": "stn",
+        "id": "site-125",
+        "text": "125",
+        "top": 450,
+        "left": 1399,
+        "width": 60,
+        "height": 63
+    }, {
+        "type": "stn",
+        "id": "site-119",
+        "text": "119",
+        "top": 353,
+        "left": 1461,
+        "width": 60,
+        "height": 63
+    }, {
+        "type": "stn",
+        "id": "site-118",
+        "text": "118",
+        "top": 353,
+        "left": 1399,
+        "width": 60,
+        "height": 63
+    }, {
+        "type": "stn",
+        "id": "site-136",
+        "text": "136",
+        "top": 611,
+        "left": 1461,
+        "width": 120,
+        "height": 30
+    }, {
+        "type": "stn",
+        "id": "site-135",
+        "text": "135",
+        "top": 611,
+        "left": 1399,
+        "width": 60,
+        "height": 30
+    }, {
+        "type": "stn",
+        "id": "site-134",
+        "text": "134",
+        "top": 611,
+        "left": 1337,
+        "width": 60,
+        "height": 30
+    }, {
+        "type": "stn",
+        "id": "site-133",
+        "text": "133",
+        "top": 611,
+        "left": 1215,
+        "width": 120,
+        "height": 30
+    }, {
+        "type": "stn",
+        "id": "site-130",
+        "text": "130",
+        "top": 515,
+        "left": 1461,
+        "width": 60,
+        "height": 30
+    }, {
+        "type": "stn",
+        "id": "site-129",
+        "text": "129",
+        "top": 515,
+        "left": 1399,
+        "width": 60,
+        "height": 30
+    }, {
+        "type": "stn",
+        "id": "site-128",
+        "text": "128",
+        "top": 515,
+        "left": 1337,
+        "width": 60,
+        "height": 30
+    }, {
+        "type": "stn",
+        "id": "site-127",
+        "text": "127",
+        "top": 515,
+        "left": 1215,
+        "width": 120,
+        "height": 30
+    }, {
+        "type": "stn",
+        "id": "site-131",
+        "text": "131",
+        "top": 515,
+        "left": 1522,
+        "width": 60,
+        "height": 30
+    }, {
+        "type": "stn",
+        "id": "site-123",
+        "text": "123",
+        "top": 418,
+        "left": 1461,
+        "width": 60,
+        "height": 30
+    }, {
+        "type": "stn",
+        "id": "site-122",
+        "text": "122",
+        "top": 418,
+        "left": 1399,
+        "width": 60,
+        "height": 30
+    }, {
+        "type": "stn",
+        "id": "site-121",
+        "text": "121",
+        "top": 418,
+        "left": 1337,
+        "width": 60,
+        "height": 30
+    }, {
+        "type": "stn",
+        "id": "site-120",
+        "text": "120",
+        "top": 418,
+        "left": 1215,
+        "width": 120,
+        "height": 30
+    }, {
+        "type": "stn",
+        "id": "site-124",
+        "text": "124",
+        "top": 418,
+        "left": 1522,
+        "width": 60,
+        "height": 30
+    }, {
+        "type": "stn",
         "id": "site-112",
         "text": "112",
-        "top": 458,
-        "left": 311,
-        "width": 100,
-        "height": 24
+        "top": 256,
+        "left": 1461,
+        "width": 60,
+        "height": 63
     }, {
         "type": "stn",
         "id": "site-111",
         "text": "111",
-        "top": 458,
-        "left": 209,
-        "width": 100,
-        "height": 24
+        "top": 256,
+        "left": 1399,
+        "width": 60,
+        "height": 63
+    }, {
+        "type": "stn",
+        "id": "site-116",
+        "text": "116",
+        "top": 321,
+        "left": 1461,
+        "width": 60,
+        "height": 30
+    }, {
+        "type": "stn",
+        "id": "site-115",
+        "text": "115",
+        "top": 321,
+        "left": 1399,
+        "width": 60,
+        "height": 30
     }, {
         "type": "stn",
         "id": "site-114",
         "text": "114",
-        "top": 568,
-        "left": 311,
-        "width": 100,
-        "height": 24
+        "top": 321,
+        "left": 1337,
+        "width": 60,
+        "height": 30
     }, {
         "type": "stn",
         "id": "site-113",
         "text": "113",
-        "top": 568,
-        "left": 209,
-        "width": 100,
-        "height": 24
+        "top": 321,
+        "left": 1215,
+        "width": 120,
+        "height": 30
+    }, {
+        "type": "stn",
+        "id": "site-117",
+        "text": "117",
+        "top": 321,
+        "left": 1522,
+        "width": 60,
+        "height": 30
     }, {
         "type": "stn",
         "id": "site-105",
         "text": "105",
-        "top": 262,
-        "left": 201,
-        "width": 100,
-        "height": 24
+        "top": 159,
+        "left": 1399,
+        "width": 60,
+        "height": 63
     }, {
         "type": "stn",
         "id": "site-109",
         "text": "109",
-        "top": 399,
-        "left": 208,
-        "width": 100,
-        "height": 24
+        "top": 224,
+        "left": 1461,
+        "width": 60,
+        "height": 30
     }, {
         "type": "stn",
         "id": "site-108",
         "text": "108",
-        "top": 318,
-        "left": 303,
-        "width": 100,
-        "height": 24
+        "top": 224,
+        "left": 1399,
+        "width": 60,
+        "height": 30
     }, {
         "type": "stn",
         "id": "site-107",
         "text": "107",
-        "top": 318,
-        "left": 201,
-        "width": 100,
-        "height": 24
+        "top": 224,
+        "left": 1337,
+        "width": 60,
+        "height": 30
     }, {
         "type": "stn",
         "id": "site-106",
         "text": "106",
-        "top": 262,
-        "left": 303,
-        "width": 100,
-        "height": 24
+        "top": 224,
+        "left": 1215,
+        "width": 120,
+        "height": 30
     }, {
         "type": "stn",
         "id": "site-104",
         "text": "104",
-        "top": 180,
-        "left": 255,
-        "width": 100,
-        "height": 24
+        "top": 128,
+        "left": 1461,
+        "width": 120,
+        "height": 30
     }, {
         "type": "stn",
         "id": "site-103",
         "text": "103",
-        "top": 180,
-        "left": 153,
-        "width": 100,
-        "height": 24
+        "top": 128,
+        "left": 1399,
+        "width": 60,
+        "height": 30
     }, {
         "type": "stn",
         "id": "site-102",
         "text": "102",
-        "top": 122,
-        "left": 255,
-        "width": 100,
-        "height": 24
+        "top": 128,
+        "left": 1337,
+        "width": 60,
+        "height": 30
     }, {
         "type": "stn",
         "id": "site-101",
         "text": "101",
-        "top": 122,
-        "left": 153,
-        "width": 100,
-        "height": 24
+        "top": 128,
+        "left": 1215,
+        "width": 120,
+        "height": 30
     }, {
         "type": "stn",
         "id": "site-110",
         "text": "110",
-        "top": 399,
-        "left": 310,
-        "width": 100,
-        "height": 24
+        "top": 224,
+        "left": 1522,
+        "width": 60,
+        "height": 30
     }]
 }
 

--
Gitblit v1.9.1