自动化立体仓库 - WCS系统
Junjie
2023-05-18 5ac625f324a7bb1656b319348cabfd13467467ef
数据源配置,通过ui配置application.yml
11个文件已添加
3个文件已修改
1226 ■■■■ 已修改文件
pom.xml 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/asrs/controller/DataResourceController.java 147 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/asrs/entity/DataResource.java 75 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/asrs/mapper/DataResourceMapper.java 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/asrs/service/DataResourceService.java 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/asrs/service/impl/DataResourceServiceImpl.java 49 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/common/model/QuotedString.java 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/common/utils/CommonUtils.java 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/common/utils/MyRepresenter.java 27 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/common/utils/YamlUtils.java 205 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/application-prod.yml 106 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/application.yml 136 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/DataResourceMapper.xml 31 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/webapp/views/dataSource/dataSource.html 368 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pom.xml
@@ -182,6 +182,16 @@
            <version>1.1.3.3</version>
            <systemPath>${project.basedir}/src/main/resources/lib/xpp3-1.1.3.3.jar</systemPath>
        </dependency>
        <dependency>
            <groupId>org.yaml</groupId>
            <artifactId>snakeyaml</artifactId>
            <version>1.33</version>
        </dependency>
        <dependency>
            <groupId>commons-io</groupId>
            <artifactId>commons-io</artifactId>
            <version>2.11.0</version>
        </dependency>
    </dependencies>
    <build>
src/main/java/com/zy/asrs/controller/DataResourceController.java
New file
@@ -0,0 +1,147 @@
package com.zy.asrs.controller;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.mapper.EntityWrapper;
import com.baomidou.mybatisplus.mapper.Wrapper;
import com.baomidou.mybatisplus.plugins.Page;
import com.core.common.DateUtils;
import com.zy.asrs.entity.DataResource;
import com.zy.asrs.service.DataResourceService;
import com.core.annotations.ManagerAuth;
import com.core.common.BaseRes;
import com.core.common.Cools;
import com.core.common.R;
import com.zy.common.utils.YamlUtils;
import com.zy.common.web.BaseController;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.*;
@RestController
public class DataResourceController extends BaseController {
    @Autowired
    private DataResourceService dataResourceService;
    @Autowired
    private YamlUtils yamlUtils;
    @RequestMapping(value = "/dataResource/{id}/auth")
    @ManagerAuth
    public R get(@PathVariable("id") String id) {
        return R.ok(dataResourceService.selectById(String.valueOf(id)));
    }
    @RequestMapping(value = "/dataResource/list/auth")
    @ManagerAuth
    public R list(@RequestParam(defaultValue = "1")Integer curr,
                  @RequestParam(defaultValue = "10")Integer limit,
                  @RequestParam(required = false)String orderByField,
                  @RequestParam(required = false)String orderByType,
                  @RequestParam Map<String, Object> param){
        EntityWrapper<DataResource> wrapper = new EntityWrapper<>();
        excludeTrash(param);
        convert(param, wrapper);
        if (!Cools.isEmpty(orderByField)){wrapper.orderBy(humpToLine(orderByField), "asc".equals(orderByType));}
        return R.ok(dataResourceService.selectPage(new Page<>(curr, limit), wrapper));
    }
    @RequestMapping(value = "/dataResource/tree/auth")
    @ManagerAuth
    public R tree(){
        return R.parse("0-操作成功").add(dataResourceService.selectAll());
    }
    private <T> void convert(Map<String, Object> map, EntityWrapper<T> wrapper){
        for (Map.Entry<String, Object> entry : map.entrySet()){
            String val = String.valueOf(entry.getValue());
            if (val.contains(RANGE_TIME_LINK)){
                String[] dates = val.split(RANGE_TIME_LINK);
                wrapper.ge(entry.getKey(), DateUtils.convert(dates[0]));
                wrapper.le(entry.getKey(), DateUtils.convert(dates[1]));
            } else {
                wrapper.like(entry.getKey(), val);
            }
        }
    }
    @RequestMapping(value = "/dataResource/add/auth")
    @ManagerAuth
    public R add(DataResource dataResource) {
        dataResourceService.insert(dataResource);
        return R.ok();
    }
    @RequestMapping(value = "/dataResource/update/auth")
    @ManagerAuth
    public R update(DataResource dataResource){
        if (Cools.isEmpty(dataResource) || null==dataResource.getId()){
            return R.error();
        }
        dataResourceService.updateById(dataResource);
        return R.ok();
    }
    @RequestMapping(value = "/dataResource/delete/auth")
    @ManagerAuth
    public R delete(@RequestParam(value="ids[]") Integer[] ids){
         for (Integer id : ids){
            dataResourceService.deleteById(id);
        }
        return R.ok();
    }
    @RequestMapping(value = "/dataResource/export/auth")
    @ManagerAuth
    public R export(@RequestBody JSONObject param){
        EntityWrapper<DataResource> wrapper = new EntityWrapper<>();
        List<String> fields = JSONObject.parseArray(param.getJSONArray("fields").toJSONString(), String.class);
        Map<String, Object> map = excludeTrash(param.getJSONObject("dataResource"));
        convert(map, wrapper);
        List<DataResource> list = dataResourceService.selectList(wrapper);
        return R.ok(exportSupport(list, fields));
    }
    @RequestMapping(value = "/dataResourceQuery/auth")
    @ManagerAuth
    public R query(String condition) {
        EntityWrapper<DataResource> wrapper = new EntityWrapper<>();
        wrapper.like("id", condition);
        Page<DataResource> page = dataResourceService.selectPage(new Page<>(0, 10), wrapper);
        List<Map<String, Object>> result = new ArrayList<>();
        for (DataResource dataResource : page.getRecords()){
            Map<String, Object> map = new HashMap<>();
            map.put("id", dataResource.getId());
            map.put("value", dataResource.getId());
            result.add(map);
        }
        return R.ok(result);
    }
    @RequestMapping(value = "/dataResource/check/column/auth")
    @ManagerAuth
    public R query(@RequestBody JSONObject param) {
        Wrapper<DataResource> wrapper = new EntityWrapper<DataResource>().eq(humpToLine(String.valueOf(param.get("key"))), param.get("val"));
        if (null != dataResourceService.selectOne(wrapper)){
            return R.parse(BaseRes.REPEAT).add(getComment(DataResource.class, String.valueOf(param.get("key"))));
        }
        return R.ok();
    }
    @RequestMapping(value = "/dataResource/pull/auth")
    @ManagerAuth
    public R pull() {
        //删除全部数据
        dataResourceService.delete(new EntityWrapper<>());
        //拉取数据
        yamlUtils.read();
        return R.ok();
    }
    @RequestMapping(value = "/dataResource/save/auth")
    public R save() {
        yamlUtils.update();
        return R.ok();
    }
}
src/main/java/com/zy/asrs/entity/DataResource.java
New file
@@ -0,0 +1,75 @@
package com.zy.asrs.entity;
import com.core.common.Cools;import com.baomidou.mybatisplus.annotations.TableId;
import com.baomidou.mybatisplus.enums.IdType;
import com.baomidou.mybatisplus.annotations.TableField;
import java.text.SimpleDateFormat;
import java.util.Date;
import org.springframework.format.annotation.DateTimeFormat;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import com.baomidou.mybatisplus.annotations.TableName;
import java.io.Serializable;
@Data
@TableName("wcs_data_resource")
public class DataResource implements Serializable {
    private static final long serialVersionUID = 1L;
    @ApiModelProperty(value= "")
    @TableId(value = "id", type = IdType.AUTO)
    private Integer id;
    @ApiModelProperty(value= "")
    private String data;
    @ApiModelProperty(value= "")
    private String name;
    @ApiModelProperty(value= "")
    @TableField("resource_id")
    private Integer resourceId;
    @ApiModelProperty(value= "")
    @TableField("create_time")
    @DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss")
    private Date createTime;
    @ApiModelProperty(value= "备注")
    private String memo;
    public DataResource() {}
    public DataResource(String data,String name,Integer resourceId,Date createTime,String memo) {
        this.data = data;
        this.name = name;
        this.resourceId = resourceId;
        this.createTime = createTime;
        this.memo = memo;
    }
//    DataResource dataResource = new DataResource(
//            null,    //
//            null,    //
//            null,    //
//            null    //
//    );
    public String getCreateTime$(){
        if (Cools.isEmpty(this.createTime)){
            return "";
        }
        return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(this.createTime);
    }
    public String getMemo() {
        if (Cools.isEmpty(this.memo)) {
            return "";
        }
        return this.memo;
    }
}
src/main/java/com/zy/asrs/mapper/DataResourceMapper.java
New file
@@ -0,0 +1,20 @@
package com.zy.asrs.mapper;
import com.zy.asrs.entity.DataResource;
import com.baomidou.mybatisplus.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
import org.springframework.stereotype.Repository;
import java.util.List;
@Mapper
@Repository
public interface DataResourceMapper extends BaseMapper<DataResource> {
    List<DataResource> selectRootData();
    List<DataResource> selectByResourceId(Integer resourceId);
    List<DataResource> selectAll();
}
src/main/java/com/zy/asrs/service/DataResourceService.java
New file
@@ -0,0 +1,19 @@
package com.zy.asrs.service;
import com.zy.asrs.entity.DataResource;
import com.baomidou.mybatisplus.service.IService;
import java.util.List;
public interface DataResourceService extends IService<DataResource> {
    List<DataResource> selectRootData();
    List<DataResource> selectByResourceId(Integer resourceId);
    //是否有子节点
    boolean hasChild(Integer resourceId);
    List<DataResource> selectAll();
}
src/main/java/com/zy/asrs/service/impl/DataResourceServiceImpl.java
New file
@@ -0,0 +1,49 @@
package com.zy.asrs.service.impl;
import com.zy.asrs.mapper.DataResourceMapper;
import com.zy.asrs.entity.DataResource;
import com.zy.asrs.service.DataResourceService;
import com.baomidou.mybatisplus.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
@Service("dataResourceService")
public class DataResourceServiceImpl extends ServiceImpl<DataResourceMapper, DataResource> implements DataResourceService {
    @Override
    public List<DataResource> selectRootData() {
        return this.baseMapper.selectRootData();
    }
    @Override
    public List<DataResource> selectByResourceId(Integer resourceId) {
        return this.baseMapper.selectByResourceId(resourceId);
    }
    @Override
    public boolean hasChild(Integer resourceId) {
        List<DataResource> resources = this.baseMapper.selectByResourceId(resourceId);
        return resources.size() > 0;
    }
    @Override
    public List<DataResource> selectAll() {
        List<DataResource> dataResources = this.baseMapper.selectAll();
        ArrayList<DataResource> list = new ArrayList<>();//普通根节点
        ArrayList<DataResource> listChild = new ArrayList<>();//有下级根节点
        for (DataResource dataResource : dataResources) {//将普通数据和有孩子节点数据进行分割
            if (this.hasChild(dataResource.getId())) {
                listChild.add(dataResource);
            }else {
                list.add(dataResource);
            }
        }
        for (DataResource dataResource : listChild) {//合并数据
            list.add(dataResource);
        }
        return list;
    }
}
src/main/java/com/zy/common/model/QuotedString.java
New file
@@ -0,0 +1,9 @@
package com.zy.common.model;
public class QuotedString {
    public String value;
    public QuotedString(String value) {
        this.value = value;
    }
}
src/main/java/com/zy/common/utils/CommonUtils.java
@@ -27,4 +27,28 @@
        }
    }
    public static boolean isNumeric(String str) {
        // 如果字符串为空,直接返回false
        if (str == null || str.length() == 0) {
            return false;
        }
        // 用正则表达式匹配数字
        return str.matches("-?\\d+(\\.\\d+)?");
    }
    public static boolean isBoolean(String str) {
        if (str == null) {
            return false;
        }
        if (str.length() == 4 || str.length() == 5) {
            if (str.equals("true") || str.equals("false")) {
                return true;
            }
        }
        return false;
    }
}
src/main/java/com/zy/common/utils/MyRepresenter.java
New file
@@ -0,0 +1,27 @@
package com.zy.common.utils;
import com.zy.common.model.QuotedString;
import org.yaml.snakeyaml.DumperOptions;
import org.yaml.snakeyaml.nodes.Node;
import org.yaml.snakeyaml.nodes.Tag;
import org.yaml.snakeyaml.representer.Represent;
import org.yaml.snakeyaml.representer.Representer;
public class MyRepresenter extends Representer {
    public MyRepresenter(){
        this.representers.put(QuotedString.class,new RepresenterQuotedString());
    }
    public class RepresenterQuotedString implements Represent {
        @Override
        public Node representData(Object o) {
            if (o instanceof QuotedString) {
                QuotedString str = (QuotedString) o;
                return representScalar(Tag.STR, str.value, DumperOptions.ScalarStyle.DOUBLE_QUOTED);
            }
            return null;
        }
    }
}
src/main/java/com/zy/common/utils/YamlUtils.java
New file
@@ -0,0 +1,205 @@
package com.zy.common.utils;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.mapper.EntityWrapper;
import com.zy.asrs.entity.DataResource;
import com.zy.asrs.service.DataResourceService;
import org.apache.commons.io.FileUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.yaml.snakeyaml.Yaml;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.util.*;
@Component
public class YamlUtils {
    @Autowired
    private DataResourceService dataResourceService;
    private static final String src = "src/main/resources/application-prod.yml";
    private final static String C = "#";
    private final static String CHARSET = "UTF-8";
    //读取后的每行数据
    private static List<String> LINES = null;
    public Map<String, Object> read() {
        Yaml yaml = new Yaml();
        try {
            LINES = FileUtils.readLines(new File(src), CHARSET);//读取后的每行数据
            Map<String, Object> resultMap = (Map<String, Object>) yaml.load(new FileInputStream(new File(src)));
            Map<String, Object> wcs = (Map<String, Object>) resultMap.get("wcs-slave");
            recursion(wcs, null);
            return wcs;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
    public void recursion(Map<String, Object> resultMap, DataResource resource) {
        Iterator<String> iterator = resultMap.keySet().iterator();
        while (iterator.hasNext()) {
            String key = iterator.next();
            Object value = resultMap.get(key);
            DataResource dataResource = new DataResource();
            if (value == null) {
                dataResource.setData(null);
            } else if (value instanceof Map) {
                dataResource.setData(JSON.toJSONString(value));
            }else {
                dataResource.setData(value.toString());
            }
            dataResource.setCreateTime(new Date());
            dataResource.setName(key);
            if (resource != null) {
                dataResource.setResourceId(resource.getId());
            }
            //获取备注
            String memo = getMemoFromLines(key);
            dataResource.setMemo(memo);
            dataResourceService.insert(dataResource);
            if (value instanceof Map) {
                Map<String, Object> map = (Map<String, Object>) resultMap.get(key);
                recursion(map, dataResource);
            }
        }
    }
    /**
     * 从读取的数据中获取备注信息
     */
    public String getMemoFromLines(String name) {
        for (int i = 0; i < LINES.size(); i++) {
            String line = LINES.get(i);
            String trim = line.trim();
            String[] split1 = trim.split(":");
            String lineName = split1[0];
            if (!lineName.equals(name)) {
                continue;
            }
            if (line.contains(C)) {
                String[] split = trim.split(C);
                String memo = split[1];//获取注释
                return memo;
            }
        }
        return null;
    }
    /**
     * 设置备注到字符串list
     */
    public String setMemoToLines(String[] lines) {
        List<DataResource> dataResources = dataResourceService.selectList(new EntityWrapper<DataResource>().ne("memo", ""));
        for (DataResource dataResource : dataResources) {
            String name = dataResource.getName();
            int idx = 0;
            for (String line : lines) {
                String trim = line.trim();
                String[] split = trim.split(":");
                String lineName = split[0];
                if (lineName.equals(name)) {
                    lines[idx] = line + " #" + dataResource.getMemo();
                }
                idx++;
            }
        }
        StringBuffer sb = new StringBuffer();
        for (String line : lines) {
            sb.append(line).append("\n");
        }
        return sb.toString();
    }
    public void update() {
        Map<String, Object> ymlData = getYmlData();
        HashMap<String, Object> hashMap = new HashMap<>();
        hashMap.put("wcs-slave", ymlData);
        save(hashMap);
    }
    /**
     * 将配置信息保存至文件中
     */
    public void save(Map<String, Object> resultMap) {
        try {
            Yaml yaml = new Yaml();
            FileWriter fileWriter = new FileWriter(new File(src));
            String dumpAsMap = yaml.dumpAsMap(resultMap);
            dumpAsMap = dumpAsMap.replaceAll("'", "");
            String[] lines = dumpAsMap.split("\n");
            dumpAsMap = setMemoToLines(lines);
            fileWriter.write(dumpAsMap);
            fileWriter.flush();
            fileWriter.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    /**
     * 遍历数据库数据,转换成yml数据
     */
    public Map<String, Object> getYmlData() {
        HashMap<String, Object> hashMap = new HashMap<>();
        List<DataResource> dataResources = dataResourceService.selectRootData();
        for (DataResource dataResource : dataResources) {
            getYmlDataRecursion(dataResource, hashMap);
        }
        return hashMap;
    }
    public void getYmlDataRecursion(DataResource dataResource, Map<String, Object> map) {
        List<DataResource> resources = dataResourceService.selectByResourceId(dataResource.getId());
        if (resources.size() > 0) {
            HashMap<String, Object> hashMap = new HashMap<>();
            map.put(dataResource.getName(), hashMap);
            for (DataResource resource : resources) {
                getYmlDataRecursion(resource, hashMap);
            }
        } else {
            String data = dataResource.getData();
            map.put(dataResource.getName(), data);
        }
    }
    public static void main(String[] args) {
        Map<String, Integer> keyCountMap = new HashMap<>();
        try {
            List<String> lines = FileUtils.readLines(new File(src), CHARSET);
            for (int i = 0; i < lines.size(); i++) {
                String line = lines.get(i);
                if (line.contains(C)) {
                    String[] split = line.split(C);
                    String tmp = split[0];
                    String memo = split[1];//获取注释
                    String[] split1 = tmp.split(":");
                    String name = split1[0];
                    String data = split1[1];
                    System.out.println(memo);
                    System.out.println(name);
                    System.out.println(data);
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
src/main/resources/application-prod.yml
New file
@@ -0,0 +1,106 @@
wcs-slave:
  crn[0]: #堆垛机1
    rack: 0
    offset: 2 #偏移量,当堆垛机站点列号=1时,偏移量=2   #偏移量,当堆垛机站点列号=1时,偏移量=2
    port: 102
    crnOutStn[1]: #堆垛机出库站点   #堆垛机出库站点
      staNo: 210
      row: 1
      bay: 41
      lev: 1
      devpPlcId: ${wcs-slave.devp[0].id}
    crnInStn[1]: #堆垛机入库站点2   #堆垛机入库站点2
      staNo: 209
      row: 2
      bay: 41
      lev: 1
      devpPlcId: ${wcs-slave.devp[0].id}
    crnOutStn[0]: #堆垛机出库站点   #堆垛机出库站点
      staNo: 105
      row: 1
      bay: 1
      lev: 1
      devpPlcId: ${wcs-slave.devp[0].id}
    ip: 10.10.10.3
    crnInStn[0]: #堆垛机入库站点1   #堆垛机入库站点1
      staNo: 106
      row: 2
      bay: 1
      lev: 1
      devpPlcId: ${wcs-slave.devp[0].id}
    id: 1
    slot: 0
    demo: false
  crn[1]: #堆垛机2
    rack: 0
    offset: 2 #偏移量,当堆垛机站点列号=1时,偏移量=2   #偏移量,当堆垛机站点列号=1时,偏移量=2
    port: 102
    crnOutStn[1]: #堆垛机出库站点   #堆垛机出库站点
      staNo: 206
      row: 3
      bay: 41
      lev: 1
      devpPlcId: ${wcs-slave.devp[0].id}
    crnInStn[1]: #堆垛机入库站点2   #堆垛机入库站点2
      staNo: 205
      row: 4
      bay: 41
      lev: 1
      devpPlcId: ${wcs-slave.devp[0].id}
    crnOutStn[0]: #堆垛机出库站点   #堆垛机出库站点
      staNo: 109
      row: 3
      bay: 1
      lev: 1
      devpPlcId: ${wcs-slave.devp[0].id}
    ip: 10.10.10.10
    crnInStn[0]: #堆垛机入库站点1   #堆垛机入库站点1
      staNo: 110
      row: 4
      bay: 1
      lev: 1
      devpPlcId: ${wcs-slave.devp[0].id}
    id: 2
    slot: 0
    demo: false
  doubleLocs:  #双深库位排号 1,4,5,8,9,12,13,16
  barcode[1]:
    port: 51236
    ip: 10.10.10.52
    id: 2
  groupCount: 2 #一个堆垛机负责的货架排数
  devp[0]: #输送线
    emptyInSta[1]: #空板入库口2
      staNo: 201
    rack: 0
    emptyInSta[0]: #空板入库口1
      staNo: 101
    port: 102
    inSta[0]: #入库口1
      scale: ${wcs-slave.scale[0].id}
      staNo: 101
      barcode: ${wcs-slave.barcode[0].id}
    ip: 10.10.10.30
    inSta[1]: #入库口2
      scale: ${wcs-slave.scale[1].id}
      staNo: 201
      barcode: ${wcs-slave.barcode[1].id}
    id: 1
    slot: 0
    outSta[0]: #出库口1
      staNo: 103
    outSta[1]: #出库口2
      staNo: 203
  scale[0]: #磅秤
    port: 5005
    ip: 10.10.10.201
    id: 1
  scale[1]: #磅秤
    port: 5005
    ip: 10.10.10.202
    id: 2
  barcode[0]: #条码扫描仪
    port: 51236
    ip: 10.10.10.52
    id: 1
  doubleDeep: false #双深
src/main/resources/application.yml
@@ -18,6 +18,8 @@
    port: 6379
    database: 0
#    password: xltys1995
  profiles:
    active: prod
mybatis-plus:
  mapper-locations: classpath:mapper/*.xml
@@ -35,137 +37,3 @@
wms:
  url: localhost:8081/htwms3
# 下位机配置
wcs-slave:
  # 双深
  doubleDeep: false
  # 双深库位排号
#  1,4,5,8,9,12,13,16
  doubleLocs:
  # 一个堆垛机负责的货架排数
  groupCount: 2
  # 堆垛机1
  crn[0]:
    id: 1
    ip: 10.10.10.3
    port: 102
    rack: 0
    slot: 0
    # 偏移量,当堆垛机站点列号=1时,偏移量=2
    offset: 2
    demo: false
    # 堆垛机入库站点1
    crnInStn[0]:
      devpPlcId: ${wcs-slave.devp[0].id}
      staNo: 106
      row: 2
      bay: 1
      lev: 1
    # 堆垛机入库站点2
    crnInStn[1]:
      devpPlcId: ${wcs-slave.devp[0].id}
      staNo: 209
      row: 2
      bay: 41
      lev: 1
    # 堆垛机出库站点
    crnOutStn[0]:
      devpPlcId: ${wcs-slave.devp[0].id}
      staNo: 105
      row: 1
      bay: 1
      lev: 1
    # 堆垛机出库站点
    crnOutStn[1]:
      devpPlcId: ${wcs-slave.devp[0].id}
      staNo: 210
      row: 1
      bay: 41
      lev: 1
  # 堆垛机2
  crn[1]:
    id: 2
    ip: 10.10.10.10
    port: 102
    rack: 0
    slot: 0
    # 偏移量,当堆垛机站点列号=1时,偏移量=2
    offset: 2
    demo: false
    # 堆垛机入库站点
    crnInStn[0]:
      devpPlcId: ${wcs-slave.devp[0].id}
      staNo: 110
      row: 4
      bay: 1
      lev: 1
    # 堆垛机入库站点2
    crnInStn[1]:
      devpPlcId: ${wcs-slave.devp[0].id}
      staNo: 205
      row: 4
      bay: 41
      lev: 1
    # 堆垛机出库站点
    crnOutStn[0]:
      devpPlcId: ${wcs-slave.devp[0].id}
      staNo: 109
      row: 3
      bay: 1
      lev: 1
    crnOutStn[1]:
      devpPlcId: ${wcs-slave.devp[0].id}
      staNo: 206
      row: 3
      bay: 41
      lev: 1
  # 输送线
  devp[0]:
    id: 1
    ip: 10.10.10.30
    port: 102
    rack: 0
    slot: 0
    # 入库口1
    inSta[0]:
      staNo: 101
      barcode: ${wcs-slave.barcode[0].id}
      scale: ${wcs-slave.scale[0].id}
    # 入库口2
    inSta[1]:
      staNo: 201
      barcode: ${wcs-slave.barcode[1].id}
      scale: ${wcs-slave.scale[1].id}
    # 空板入库口1
    emptyInSta[0]:
      staNo: 101
    # 空板入库口2
    emptyInSta[1]:
      staNo: 201
    # 出库口1
    outSta[0]:
      staNo: 103
    # 出库口2
    outSta[1]:
      staNo: 203
  # 条码扫描仪
  barcode[0]:
    id: 1
    ip: 10.10.10.52
    port: 51236
  barcode[1]:
    id: 2
    ip: 10.10.10.52
    port: 51236
   # 磅秤
  scale[0]:
    id: 1
    ip: 10.10.10.201
    port: 5005
  # 磅秤
  scale[1]:
    id: 2
    ip: 10.10.10.202
    port: 5005
src/main/resources/mapper/DataResourceMapper.xml
New file
@@ -0,0 +1,31 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.zy.asrs.mapper.DataResourceMapper">
    <!-- 通用查询映射结果 -->
    <resultMap id="BaseResultMap" type="com.zy.asrs.entity.DataResource">
        <id column="id" property="id" />
        <result column="data" property="data" />
        <result column="name" property="name" />
        <result column="resource_id" property="resourceId" />
        <result column="create_time" property="createTime" />
        <result column="memo" property="memo" />
    </resultMap>
    <select id="selectRootData" resultMap="BaseResultMap">
        select * from wcs_data_resource
        where resource_id is null
    </select>
    <select id="selectByResourceId" resultMap="BaseResultMap">
        select * from wcs_data_resource
        where resource_id = #{resourceId}
    </select>
    <select id="selectAll" resultMap="BaseResultMap">
        select * from wcs_data_resource
        order by name
    </select>
</mapper>
src/main/webapp/views/dataSource/dataSource.html
New file
@@ -0,0 +1,368 @@
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <title></title>
    <meta name="renderer" content="webkit">
    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
    <link rel="stylesheet" href="../../static/wms/layui/css/layui.css" media="all">
    <link rel="stylesheet" href="../../static/wms/css/admin.css?v=318" media="all">
    <link rel="stylesheet" href="../../static/wms/css/cool.css" media="all">
    <link rel="stylesheet" href="../../static/wms/css/common.css" media="all">
    <style>
        #detail {
            padding: 25px 30px 0 0;
        }
        .ew-tree-table-box {
            height: 100%;
        }
    </style>
</head>
<body>
<!-- 正文开始 -->
<div class="layui-fluid">
    <div class="layui-card">
        <div class="layui-card-body">
            <!-- 数据表格 -->
            <table id="dataResource"></table>
        </div>
    </div>
</div>
<script type="text/html" id="toolbar">
    <button lay-event="add" class="layui-btn layui-btn-sm icon-btn"><i class="layui-icon">&#xe654;</i>添加</button>
    <button lay-event="del" class="layui-btn layui-btn-sm layui-btn-danger icon-btn"><i class="layui-icon">&#xe640;</i>删除</button>
    <button lay-event="pull" class="layui-btn layui-btn-sm icon-btn">拉取数据</button>
    <button lay-event="save" class="layui-btn layui-btn-sm icon-btn">保存数据</button>
</script>
<script type="text/html" id="operate">
    <a class="layui-btn layui-btn-primary layui-btn-xs" lay-event="edit">修改</a>
    <a class="layui-btn layui-btn-danger layui-btn-xs btn-del" lay-event="del">删除</a>
</script>
<!-- 表单弹窗 -->
<script type="text/html" id="editDialog">
    <form id="detail" lay-filter="detail" class="layui-form" style="margin: 0">
        <input name="id" type="hidden">
        <input name="uuid" type="hidden">
        <input name="level" type="hidden">
        <div class="layui-row">
            <div class="layui-col-md6">
                <div class="layui-form-item">
                    <label class="layui-form-label">上级菜单</label>
                    <div class="layui-input-block">
                        <div id="dataResourceParentSel" class="ew-xmselect-tree"></div>
                    </div>
                </div>
                <div class="layui-form-item">
                    <label class="layui-form-label layui-form-required">数据</label>
                    <div class="layui-input-block">
                        <input name="data" placeholder="请输入数据" class="layui-input">
                    </div>
                </div>
            </div>
            <div class="layui-col-md6">
                <div class="layui-form-item">
                    <label class="layui-form-label layui-form-required">名称</label>
                    <div class="layui-input-block">
                        <input name="name" placeholder="请输入名称" class="layui-input" lay-vertype="tips" lay-verify="required" required="">
                    </div>
                </div>
                <div class="layui-form-item">
                    <label class="layui-form-label">备注</label>
                    <div class="layui-input-block">
                        <input name="memo" placeholder="请输入备注" class="layui-input">
                    </div>
                </div>
            </div>
        </div>
        <hr class="layui-bg-gray">
        <div class="layui-form-item text-right">
            <button class="layui-btn" lay-filter="editSubmit" lay-submit="">保存</button>
            <button class="layui-btn layui-btn-primary" type="button" ew-event="closeDialog">取消</button>
        </div>
    </form>
</script>
<script type="text/html" id="typeTpl">
    {{# if( d.level === 1 ){ }}
    <span name="level" class="layui-badge layui-badge-green">菜单</span>
    {{# } else if(d.level === 2){ }}
    <span name="level" class="layui-badge layui-badge-green">菜单</span>
    {{# } else if(d.level === 3){ }}
    <span name="level" class="layui-badge layui-badge-gray">按钮</span>
    {{# } }}
</script>
<script type="text/javascript" src="../../static/wms/js/jquery/jquery-3.3.1.min.js"></script>
<script type="text/javascript" src="../../static/wms/layui/layui.js" charset="utf-8"></script>
<script type="text/javascript" src="../../static/wms/js/common.js" charset="utf-8"></script>
<script type="text/javascript" src="../../static/wms/js/cool.js" charset="utf-8"></script>
<script>
    layui.config({
        base: baseUrl + "/static/wms/layui/lay/modules/"
    }).use(['form','treeTable', 'admin', 'xmSelect'], function() {
        var $ = layui.jquery;
        var layer = layui.layer;
        var form = layui.form;
        var admin = layui.admin;
        var treeTable = layui.treeTable;
        var xmSelect = layui.xmSelect;
        var tbDataList = [];
        var insTb = treeTable.render({
            elem: '#dataResource',
            url: baseUrl+'/dataResource/tree/auth',
            headers: {token: localStorage.getItem('token')},
            height: 'full-200',
            toolbar: '#toolbar',
            tree: {
                iconIndex: 2,           // 折叠图标显示在第几列
                isPidData: true,        // 是否是id、pid形式数据
                idName: 'id',           // id字段名称
                pidName: 'resourceId'     // pid字段名称
            },
            cols: [[
                {type: 'checkbox', fixed: 'left'}
                ,{field: 'id', title: 'ID', sort: true,align: 'center', fixed: 'left', width: 80, hide: true}
                ,{field: 'name', align: 'left',title: '名称'}
                ,{field: 'data', align: 'center',title: '数据', edit:'text'}
                ,{field: 'memo', align: 'center',title: '备注', edit:'text'}
                ,{field: 'createTime$', align: 'center',title: '时间'}
                ,{fixed: 'right', title:'操作', align: 'center', toolbar: '#operate', width:150}
            ]],
            done: function (data) {
                $('.ew-tree-table-box').css('height', '100%');
                // insTb.expandAll();
                tbDataList = data;
                limit();
            }
        });
        /* 表格头工具栏点击事件 */
        treeTable.on('toolbar(dataResource)', function (obj) {
            if (obj.event === 'add') { // 添加
                showEditModel();
            } else if (obj.event === 'del') { // 删除
                var checkRows = insTb.checkStatus();
                if (checkRows.length === 0) {
                    layer.msg('请选择要删除的数据', {icon: 2});
                    return;
                }
                var ids = checkRows.map(function (d) {
                    if (!d.LAY_INDETERMINATE) {
                        return d.id;
                    } else {
                        return null;
                    }
                });
                doDel({ids: ids});
            }else if (obj.event === 'pull') {//拉取数据
                var loadIndex = layer.load(2);
                $.ajax({
                    url: baseUrl+"/dataResource/pull/auth",
                    headers: {'token': localStorage.getItem('token')},
                    data: {},
                    method: 'POST',
                    success: function (res) {
                        layer.close(loadIndex);
                        if (res.code === 200){
                            layer.msg(res.msg, {icon: 1});
                            insTb.refresh();
                        } else if (res.code === 403){
                            top.location.href = baseUrl+"/";
                        }else {
                            layer.msg(res.msg, {icon: 2});
                        }
                    }
                })
            }else if (obj.event === 'save') {//保存数据
                var loadIndex = layer.load(2);
                $.ajax({
                    url: baseUrl+"/dataResource/save/auth",
                    headers: {'token': localStorage.getItem('token')},
                    data: {},
                    method: 'POST',
                    success: function (res) {
                        layer.close(loadIndex);
                        if (res.code === 200){
                            layer.msg(res.msg, {icon: 1});
                            insTb.refresh();
                        } else if (res.code === 403){
                            top.location.href = baseUrl+"/";
                        }else {
                            layer.msg(res.msg, {icon: 2});
                        }
                    }
                })
            }
        });
        /* 表格操作列点击事件 */
        treeTable.on('tool(dataResource)', function (obj) {
            if (obj.event === 'edit') { // 修改
                showEditModel(obj.data);
            } else if (obj.event === 'del') { // 删除
                doDel(obj);
            }
        });
        // 页面修改
        treeTable.on('edit(dataResource)', function (obj) {
            let data = obj.data
            let modify = true;
            if (obj.field === 'data'){
                if (data.data == obj.value) {
                    modify = false;
                }
            }else if(obj.field === 'memo') {
                if (data.memo == obj.value) {
                    modify = false;
                }
            }
            if (modify) {
                data[obj.field] = obj.value;
                data.createTime = null;
                let loadIndex = data.LAY_INDEX
                $.ajax({
                    url: baseUrl+"/dataResource/update/auth",
                    headers: {'token': localStorage.getItem('token')},
                    data: data,
                    method: 'POST',
                    success: function (res) {
                        layer.close(loadIndex);
                        if (res.code === 200){
                            layer.msg(res.msg, {icon: 1});
                            insTb.refresh();
                            setTimeout(function () {
                                insTb.expand(data.field.resourceId);
                            }, 200)
                        } else if (res.code === 403){
                            top.location.href = baseUrl+"/";
                        }else {
                            layer.msg(res.msg, {icon: 2});
                        }
                    }
                })
            }
        });
        /* 显示表单弹窗 */
        function showEditModel(mData) {
            admin.open({
                type: 1,
                area: '600px',
                title: (mData ? '修改' : '添加') + '权限',
                content: $('#editDialog').html(),
                success: function (layero, dIndex) {
                    // 回显表单数据
                    form.val('detail', mData);
                    // 表单提交事件
                    form.on('submit(editSubmit)', function (data) {
                        data.field.resourceId = insXmSel.getValue('valueStr');
                        var loadIndex = layer.load(2);
                        $.ajax({
                            url: baseUrl+"/dataResource/"+(mData?'update':'add')+"/auth",
                            headers: {'token': localStorage.getItem('token')},
                            data: data.field,
                            method: 'POST',
                            success: function (res) {
                                layer.close(loadIndex);
                                if (res.code === 200){
                                    layer.close(dIndex);
                                    layer.msg(res.msg, {icon: 1});
                                    insTb.refresh();
                                    setTimeout(function () {
                                        insTb.expand(data.field.resourceId);
                                    }, 200)
                                } else if (res.code === 403){
                                    top.location.href = baseUrl+"/";
                                }else {
                                    layer.msg(res.msg, {icon: 2});
                                }
                            }
                        })
                        return false;
                    });
                    // 渲染下拉树
                    var insXmSel = xmSelect.render({
                        el: '#dataResourceParentSel',
                        height: '250px',
                        data: insTb.options.data,
                        initValue: mData&&mData.resourceId!=null ? [mData.resourceId] : [],
                        model: {label: {type: 'text'}},
                        prop: {
                            name: 'name',
                            value: 'id'
                        },
                        radio: true,
                        clickClose: true,
                        tree: {
                            show: true,
                            indent: 15,
                            strict: false,
                            expandedKeys: false
                        }
                    });
                    // 弹窗不出现滚动条
                    $(layero).children('.layui-layer-content').css('overflow', 'visible');
                    layui.form.render('select');
                }
            });
        }
        /* 删除 */
        function doDel(obj) {
            layer.confirm('确定要删除选中数据吗?', {
                skin: 'layui-layer-admin',
                shade: .1
            }, function (i) {
                layer.close(i);
                var loadIndex = layer.load(2);
                var ids;
                if (obj.data) {
                    ids = [];
                    ids[0] = obj.data.id;
                } else {
                    ids = obj.ids;
                }
                $.ajax({
                    url: baseUrl+"/dataResource/delete/auth",
                    headers: {'token': localStorage.getItem('token')},
                    data: {ids: ids},
                    method: 'POST',
                    success: function (res) {
                        layer.close(loadIndex);
                        if (res.code === 200){
                            layer.msg(res.msg, {icon: 1});
                            insTb.refresh();
                        } else if (res.code === 403){
                            top.location.href = baseUrl+"/";
                        } else {
                            layer.msg(res.msg, {icon: 2});
                        }
                    }
                })
            });
        }
    });
</script>
</body>
</html>