New file |
| | |
| | | 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 { |
| | | // 管道中断,fireChannelRead未执行,需要手动释放堆外内存 |
| | | 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; |
| | | } |
New file |
| | |
| | | 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管道 |
| | | * 控制所有netty handler流向 |
| | | * 待完成: 动态管理handler |
| | | * 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) |
| | | // 解码器 |
| | | .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); |
| | | } |
| | | |
| | | } |
New file |
| | |
| | | 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()); |
| | | } |
| | | |
| | | } |
New file |
| | |
| | | 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); |
| | | |
| | | } |
New file |
| | |
| | | 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; |
| | | } |
| | | |
| | | |
| | | |
| | | } |
New file |
| | |
| | | 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"); |
| | | |
| | | } |
New file |
| | |
| | | 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; |
| | | |
| | | /** |
| | | * 消息的校正码 |
| | | */ |
| | | 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; |
| | | // } |
| | | |
| | | } |
New file |
| | |
| | | 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("读码器【IP:{}】 上行数据 ===>> {}", 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); |
| | | } |
| | | } |
New file |
| | |
| | | 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(); |
| | | } |
| | | |
| | | } |
New file |
| | |
| | | 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); |
| | | |
| | | // 解析 |
| | | 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); |
| | | } |
| | | |
| | | } |
New file |
| | |
| | | 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); |
| | | // } |
| | | } |
New file |
| | |
| | | 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; |
| | | |
| | | } |
| | |
| | | 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; |
| | | |
| | | /** |
| | | * 条码数量 |
| | | */ |
| | |
| | | } |
| | | |
| | | 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++) { |
| | |
| | | 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 |
| | | }] |
| | | } |
| | | |