zhangqian
2023-08-24 602d118cbe5d5a6910766c3e83208f09c0ba0bb6
增加设置生产总量接口
2个文件已添加
8个文件已修改
582 ■■■■■ 已修改文件
api/v1/plc.go 92 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
constvar/const.go 13 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
docs/docs.go 58 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
docs/swagger.json 58 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
docs/swagger.yaml 38 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
model/device_plc.go 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
model/response/common.go 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pkg/plc/com.go 235 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
router/index.go 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
service/plc.go 78 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
api/v1/plc.go
@@ -6,18 +6,15 @@
    _ "apsClient/model/response"
    "apsClient/pkg/contextx"
    "apsClient/pkg/ecode"
    "apsClient/pkg/logx"
    "apsClient/pkg/plc"
    "apsClient/service"
    "encoding/binary"
    "fmt"
    "github.com/gin-gonic/gin"
    "github.com/spf13/cast"
)
type PlcApi struct{}
// GetProductProgress
// @Tags      获取动态数据
// @Tags      生产数量
// @Summary   获取生产进度
// @Produce   application/json
// @Success   200   {object}  contextx.Response{data=response.ProductProgress}  "成功"
@@ -34,45 +31,56 @@
        return
    }
    var startAddress int
    var valueType string
    var dataLength int
    var ipAddr string
    for _, pc := range plcConfig.Details {
        if pc.FieldName == constvar.PlcStartAddressTypeFinishNumber {
            startAddress = pc.StartAddress
            valueType = pc.Type
            dataLength = pc.Length
        }
        ipAddr = fmt.Sprintf("%s:%v", plcConfig.Address, plcConfig.Port)
    finishNumber, err := service.PlcRead(plcConfig, constvar.PlcStartAddressTypeFinishNumber)
    if err != nil {
        ctx.FailWithMsg(ecode.UnknownErr, "读取数据失败,请检查plc配置")
        return
    }
    totalNumber, err := service.PlcRead(plcConfig, constvar.PlcStartAddressTypeTotalNumber)
    if err != nil {
        ctx.FailWithMsg(ecode.UnknownErr, "读取数据失败,请检查plc配置")
        return
    }
    resp := new(response.ProductProgress)
    if startAddress == 0 || valueType == "" {
        ctx.OkWithDetailed(resp)
        logx.Warnf("请先配置PLC")
        return
    }
    conn, err := plc.NewModbusConnection(ipAddr)
    if err != nil {
        ctx.OkWithDetailed(resp)
        logx.Errorf("GetProductProgress 连接plc失败: %v", err.Error())
        return
    }
    defer conn.Close()
    rawData, err := plc.ReadHoldingRegister(conn, startAddress, dataLength)
    if err != nil {
        ctx.OkWithDetailed(resp)
        logx.Errorf("GetProductProgress 获取plc数据失败: %v", err.Error())
        return
    }
    resp.FinishNumber = int(binary.BigEndian.Uint16(rawData))
    if err != nil {
        ctx.OkWithDetailed(resp)
        logx.Errorf("GetProductProgress 获取生产进度数据解析失败: %v, data: %v, valueType:%v", err.Error(), rawData, valueType)
        return
    }
    resp.FinishNumber = cast.ToInt(finishNumber)
    resp.TotalNumber = cast.ToInt(totalNumber)
    ctx.OkWithDetailed(resp)
}
// SetProductNumber
// @Tags      生产数量
// @Summary   设置生产总量
// @Produce   application/json
// @Success   200   {object}  contextx.Response{}  "成功"
// @Router    /v1/plc/setProductNumber [post]
func (slf *PlcApi) SetProductNumber(c *gin.Context) {
    ctx, ok := contextx.NewContext(c, nil)
    if !ok {
        return
    }
    taskData, code := service.NewTaskService().GetTask()
    if code != ecode.OK {
        ctx.Fail(code)
        return
    }
    if taskData.Order == nil {
        ctx.FailWithMsg(ecode.UnknownErr, "当前没有待生产工单")
        return
    }
    plcConfig, code := service.NewDevicePlcService().GetDevicePlc()
    if code != ecode.OK {
        ctx.FailWithMsg(ecode.UnknownErr, "请先配置PLC")
        return
    }
    err := service.PlcWrite(plcConfig, constvar.PlcStartAddressTypeFinishNumber, taskData.Order.Amount.IntPart())
    if err != nil {
        ctx.FailWithMsg(ecode.UnknownErr, "设置失败,请检查plc配置")
        return
    }
    ctx.Ok()
}
constvar/const.go
@@ -8,11 +8,18 @@
    NsqTopicProcessParamsResponse = "aps.%v.processParams.response"
)
type PlcStartAddressType string
type PlcStartAddressType int
const (
    PlcStartAddressTypeFinishNumber = 1
    PlcStartAddressTypeTotalNumber  = 2
    PlcStartAddressTypeFinishNumber PlcStartAddressType = 1
    PlcStartAddressTypeTotalNumber  PlcStartAddressType = 2
)
type PlcStartAddressValueType string
const (
    PlcStartAddressValueTypeString PlcStartAddressValueType = "string"
    PlcStartAddressValueTypeInt    PlcStartAddressValueType = "int"
)
const (
docs/docs.go
@@ -158,7 +158,7 @@
                    "application/json"
                ],
                "tags": [
                    "获取动态数据"
                    "生产数量"
                ],
                "summary": "获取生产进度",
                "responses": {
@@ -178,6 +178,25 @@
                                    }
                                }
                            ]
                        }
                    }
                }
            }
        },
        "/v1/plc/setProductNumber": {
            "post": {
                "produces": [
                    "application/json"
                ],
                "tags": [
                    "生产数量"
                ],
                "summary": "设置生产总量",
                "responses": {
                    "200": {
                        "description": "成功",
                        "schema": {
                            "$ref": "#/definitions/contextx.Response"
                        }
                    }
                }
@@ -535,6 +554,28 @@
                "PlcMethodSerial"
            ]
        },
        "constvar.PlcStartAddressType": {
            "type": "integer",
            "enum": [
                1,
                2
            ],
            "x-enum-varnames": [
                "PlcStartAddressTypeFinishNumber",
                "PlcStartAddressTypeTotalNumber"
            ]
        },
        "constvar.PlcStartAddressValueType": {
            "type": "string",
            "enum": [
                "string",
                "int"
            ],
            "x-enum-varnames": [
                "PlcStartAddressValueTypeString",
                "PlcStartAddressValueTypeInt"
            ]
        },
        "contextx.Response": {
            "type": "object",
            "properties": {
@@ -591,7 +632,11 @@
            "properties": {
                "fieldName": {
                    "description": "对应系统字段",
                    "type": "integer"
                    "allOf": [
                        {
                            "$ref": "#/definitions/constvar.PlcStartAddressType"
                        }
                    ]
                },
                "length": {
                    "description": "数据长度",
@@ -603,7 +648,11 @@
                },
                "type": {
                    "description": "数据类型",
                    "type": "string"
                    "allOf": [
                        {
                            "$ref": "#/definitions/constvar.PlcStartAddressValueType"
                        }
                    ]
                }
            }
        },
@@ -871,6 +920,9 @@
            "properties": {
                "finishNumber": {
                    "type": "integer"
                },
                "totalNumber": {
                    "type": "integer"
                }
            }
        },
docs/swagger.json
@@ -146,7 +146,7 @@
                    "application/json"
                ],
                "tags": [
                    "获取动态数据"
                    "生产数量"
                ],
                "summary": "获取生产进度",
                "responses": {
@@ -166,6 +166,25 @@
                                    }
                                }
                            ]
                        }
                    }
                }
            }
        },
        "/v1/plc/setProductNumber": {
            "post": {
                "produces": [
                    "application/json"
                ],
                "tags": [
                    "生产数量"
                ],
                "summary": "设置生产总量",
                "responses": {
                    "200": {
                        "description": "成功",
                        "schema": {
                            "$ref": "#/definitions/contextx.Response"
                        }
                    }
                }
@@ -523,6 +542,28 @@
                "PlcMethodSerial"
            ]
        },
        "constvar.PlcStartAddressType": {
            "type": "integer",
            "enum": [
                1,
                2
            ],
            "x-enum-varnames": [
                "PlcStartAddressTypeFinishNumber",
                "PlcStartAddressTypeTotalNumber"
            ]
        },
        "constvar.PlcStartAddressValueType": {
            "type": "string",
            "enum": [
                "string",
                "int"
            ],
            "x-enum-varnames": [
                "PlcStartAddressValueTypeString",
                "PlcStartAddressValueTypeInt"
            ]
        },
        "contextx.Response": {
            "type": "object",
            "properties": {
@@ -579,7 +620,11 @@
            "properties": {
                "fieldName": {
                    "description": "对应系统字段",
                    "type": "integer"
                    "allOf": [
                        {
                            "$ref": "#/definitions/constvar.PlcStartAddressType"
                        }
                    ]
                },
                "length": {
                    "description": "数据长度",
@@ -591,7 +636,11 @@
                },
                "type": {
                    "description": "数据类型",
                    "type": "string"
                    "allOf": [
                        {
                            "$ref": "#/definitions/constvar.PlcStartAddressValueType"
                        }
                    ]
                }
            }
        },
@@ -859,6 +908,9 @@
            "properties": {
                "finishNumber": {
                    "type": "integer"
                },
                "totalNumber": {
                    "type": "integer"
                }
            }
        },
docs/swagger.yaml
@@ -67,6 +67,22 @@
    x-enum-varnames:
    - PlcMethodModbusTCP
    - PlcMethodSerial
  constvar.PlcStartAddressType:
    enum:
    - 1
    - 2
    type: integer
    x-enum-varnames:
    - PlcStartAddressTypeFinishNumber
    - PlcStartAddressTypeTotalNumber
  constvar.PlcStartAddressValueType:
    enum:
    - string
    - int
    type: string
    x-enum-varnames:
    - PlcStartAddressValueTypeString
    - PlcStartAddressValueTypeInt
  contextx.Response:
    properties:
      code:
@@ -107,8 +123,9 @@
  model.DevicePlcAddress:
    properties:
      fieldName:
        allOf:
        - $ref: '#/definitions/constvar.PlcStartAddressType'
        description: 对应系统字段
        type: integer
      length:
        description: 数据长度
        type: integer
@@ -116,8 +133,9 @@
        description: 数据起始地址
        type: integer
      type:
        allOf:
        - $ref: '#/definitions/constvar.PlcStartAddressValueType'
        description: 数据类型
        type: string
    type: object
  model.NetConfig:
    properties:
@@ -299,6 +317,8 @@
    properties:
      finishNumber:
        type: integer
      totalNumber:
        type: integer
    type: object
  response.TaskData:
    properties:
@@ -409,7 +429,19 @@
              type: object
      summary: 获取生产进度
      tags:
      - 获取动态数据
      - 生产数量
  /v1/plc/setProductNumber:
    post:
      produces:
      - application/json
      responses:
        "200":
          description: 成功
          schema:
            $ref: '#/definitions/contextx.Response'
      summary: 设置生产总量
      tags:
      - 生产数量
  /v1/plcBrand/add:
    post:
      parameters:
model/device_plc.go
@@ -29,10 +29,10 @@
    }
    DevicePlcAddress struct {
        StartAddress int    `json:"startAddress"` // 数据起始地址
        Length       int    `json:"length"`       // 数据长度
        Type         string `json:"type"`         // 数据类型
        FieldName    int    `json:"fieldName"`    // 对应系统字段
        StartAddress int                               `json:"startAddress"` // 数据起始地址
        Length       int                               `json:"length"`       // 数据长度
        Type         constvar.PlcStartAddressValueType `json:"type"`         // 数据类型
        FieldName    constvar.PlcStartAddressType      `json:"fieldName"`    // 对应系统字段
    }
    DevicePlcSearch struct {
model/response/common.go
@@ -41,4 +41,5 @@
type ProductProgress struct {
    FinishNumber int `json:"finishNumber"`
    TotalNumber  int `json:"totalNumber"`
}
pkg/plc/com.go
New file
@@ -0,0 +1,235 @@
package plc
import (
    "encoding/hex"
    "errors"
    "fmt"
    "io"
    "strings"
    "time"
    "github.com/jacobsa/go-serial/serial"
)
/*
定义串口客户端
*/
type SerialClient struct {
    readWriter io.ReadWriteCloser
    OutBuffer  chan string
    PortName   string
    baudRate   uint
}
func NewSerialClient(portName string, baudRate uint) (*SerialClient, error) {
    c := &SerialClient{
        readWriter: nil,
        OutBuffer:  make(chan string, 1024),
        PortName:   portName,
        baudRate:   baudRate,
    }
    r, err := c.open(portName, baudRate)
    if err != nil {
        return nil, err
    }
    c.readWriter = r
    return c, nil
}
func (c *SerialClient) Start() {
    for {
        buf := make([]byte, 1024)
        n, err := c.readWriter.Read(buf)
        if err != nil {
            if err != io.EOF {
                //尝试重新打开
                r, err := c.open(c.PortName, c.baudRate)
                if err != nil {
                    time.Sleep(2 * time.Second)
                    continue
                }
                c.readWriter = r
            }
            continue
        }
        buf = buf[:n]
        res := hex.EncodeToString(buf)
        if strings.TrimSpace(res) == "" {
            continue
        }
        fmt.Printf("Rx: %s\n", res)
        c.OutBuffer <- res
    }
}
func (c *SerialClient) open(portName string, baudRate uint) (io.ReadWriteCloser, error) {
    options := serial.OpenOptions{
        PortName:        portName,
        BaudRate:        baudRate,
        DataBits:        7,
        StopBits:        1,
        MinimumReadSize: 4,
        ParityMode:      serial.PARITY_EVEN,
    }
    r, err := serial.Open(options)
    if err != nil {
        return nil, err
    }
    c.readWriter = r
    return r, err
}
func (c *SerialClient) Close() {
    if c.readWriter != nil {
        c.readWriter.Close()
    }
}
func (c *SerialClient) SendMsg(data string) (int, error) {
    if c.readWriter == nil {
        return 0, errors.New("serial port is closed")
    }
    bytes, err := hex.DecodeString(data)
    if err != nil {
        return 0, err
    }
    return c.readWriter.Write(bytes)
}
func (c *SerialClient) ReadHoldRegister(addr string) (int, error) {
    if c.readWriter == nil {
        return 0, errors.New("serial port is closed")
    }
    // 三菱fn系列串口协议
    // 02 表示开始 + 30 表示读数据 + 4位地址位 + 02 数据长度 + 03 停止 + 偶校验
    // 先组装数据
    // 读取的地址
    dataBytes := append([]byte{0x30}, []byte(addr)...)
    // 读取的长度
    dataBytes = append(dataBytes, []byte("02")...)
    // 停止
    dataBytes = append(dataBytes, []byte{0x03}...)
    // 计算校验码, 30 + 地址编码 + 数据长度 + 03 , 十六进制结果取后两位.
    var sum uint8
    for _, d := range dataBytes {
        sum += d
    }
    sumStr := fmt.Sprintf("%x", sum)
    fmt.Printf("sumStr: %s\n", sumStr)
    var checkCode string
    if len(sumStr) < 2 {
        checkCode = "0" + sumStr
    } else {
        checkCode = sumStr[len(sumStr)-2:]
    }
    var bytes = []byte{0x02}
    // 添加数据
    bytes = append(bytes, dataBytes...)
    // 添加校验
    bytes = append(bytes, []byte(checkCode)...)
    fmt.Printf("Tx: %X \n", bytes)
    // 写
    if n, err := c.readWriter.Write(bytes); err != nil {
        return n, err
    } else {
        fmt.Printf("Tx:len:%d\n", n)
    }
    // 读
    buf := make([]byte, 1024)
    n, err := c.readWriter.Read(buf)
    if err != nil {
        return n, err
    }
    buf = buf[:n]
    fmt.Printf("Rx: %X\nRx len:%d", buf, n)
    fmt.Printf("value: %d\n", buf[2:6])
    return n, nil
}
func (c *SerialClient) WriteHoldRegister(addr string) (int, error) {
    if c.readWriter == nil {
        return 0, errors.New("serial port is closed")
    }
    // 三菱fn系列串口协议
    // 02 表示开始 + 31 表示写数据 + 4位地址位 + 02 数据长度 + 03 停止 + 偶校验
    // 先组装数据
    // 读取的地址
    dataBytes := append([]byte{0x31}, []byte(addr)...)
    // 读取的长度
    dataBytes = append(dataBytes, []byte("02")...)
    // 停止
    dataBytes = append(dataBytes, []byte{0x03}...)
    // 计算校验码, 30 + 地址编码 + 数据长度 + 03 , 十六进制结果取后两位.
    var sum uint8
    for _, d := range dataBytes {
        sum += d
    }
    sumStr := fmt.Sprintf("%x", sum)
    fmt.Printf("sumStr: %s\n", sumStr)
    var checkCode string
    if len(sumStr) < 2 {
        checkCode = "0" + sumStr
    } else {
        checkCode = sumStr[len(sumStr)-2:]
    }
    var bytes = []byte{0x02}
    // 添加数据
    bytes = append(bytes, dataBytes...)
    // 添加校验
    bytes = append(bytes, []byte(checkCode)...)
    fmt.Printf("Tx: %X \n", bytes)
    // 写
    if n, err := c.readWriter.Write(bytes); err != nil {
        return n, err
    } else {
        fmt.Printf("Tx:len:%d\n", n)
    }
    // 读
    buf := make([]byte, 1024)
    n, err := c.readWriter.Read(buf)
    if err != nil {
        return n, err
    }
    buf = buf[:n]
    fmt.Printf("Rx: %X\nRx len:%d", buf, n)
    fmt.Printf("value: %d\n", buf[2:6])
    return n, nil
}
router/index.go
@@ -54,6 +54,7 @@
    plcGroup := v1Group.Group("plc")
    {
        plcGroup.GET("productProgress", plcApi.GetProductProgress) // 获取网络配置
        plcGroup.POST("setProductNumber", plcApi.SetProductNumber) // 下发生产总量
    }
    InitPlcBrandRouter(v1Group)
service/plc.go
New file
@@ -0,0 +1,78 @@
package service
import (
    "apsClient/constvar"
    "apsClient/model"
    "apsClient/pkg/logx"
    "apsClient/pkg/plc"
    "encoding/binary"
    "errors"
    "fmt"
)
func PlcRead(plcConfig *model.DevicePlc, fieldType constvar.PlcStartAddressType) (val interface{}, err error) {
    var (
        startAddress int
        valueType    constvar.PlcStartAddressValueType
        dataLength   int
        ipAddr       string
    )
    for _, pc := range plcConfig.Details {
        if pc.FieldName == fieldType {
            startAddress = pc.StartAddress
            valueType = pc.Type
            dataLength = pc.Length
        }
        ipAddr = fmt.Sprintf("%s:%v", plcConfig.Address, plcConfig.Port)
    }
    conn, err := plc.NewModbusConnection(ipAddr)
    if err != nil {
        logx.Errorf("GetProductProgress 连接plc失败: %v", err.Error())
        return
    }
    defer conn.Close()
    rawData, err := plc.ReadHoldingRegister(conn, startAddress, dataLength)
    if err != nil {
        logx.Errorf("GetProductProgress 获取plc数据失败: %v", err.Error())
        return
    }
    switch valueType {
    case constvar.PlcStartAddressValueTypeString:
        return string(rawData), nil
    case constvar.PlcStartAddressValueTypeInt:
        return int(binary.BigEndian.Uint16(rawData)), nil
    }
    return nil, errors.New("undefined value type")
}
func PlcWrite(plcConfig *model.DevicePlc, fieldType constvar.PlcStartAddressType, value interface{}) (err error) {
    var (
        startAddress int
        ipAddr       string
    )
    for _, pc := range plcConfig.Details {
        if pc.FieldName == fieldType {
            startAddress = pc.StartAddress
        }
        ipAddr = fmt.Sprintf("%s:%v", plcConfig.Address, plcConfig.Port)
    }
    conn, err := plc.NewModbusConnection(ipAddr)
    if err != nil {
        logx.Errorf("GetProductProgress 连接plc失败: %v", err.Error())
        return
    }
    defer conn.Close()
    result, err := plc.WriteHoldingRegister(conn, startAddress, value)
    if err != nil {
        logx.Infof("plc write failed, address: %v, value: %v, err: %v", startAddress, value, err.Error())
        return
    }
    logx.Infof("plc write ok, address: %v, value: %v, result: %v", startAddress, value, result)
    return
}