package plccom import ( "apsClient/pkg/logx" "apsClient/pkg/plccom/mitsubishi" "encoding/base64" "errors" "fmt" "io" "net/http" "strings" ) // SerialProtocol 是串口通信协议的通用接口 type SerialProtocol interface { // ConvertLabelToAddress 将设备特定的标签转换为寄存器地址 ConvertLabelToAddress(label string) string // MakeReadProtocol 创建读取数据的协议 MakeReadProtocol(addr string) []byte // MakeReadProtocolBits 创建读取数据的协议 MakeReadProtocolBits(bits int, addr string) []byte // MakeWriteProtocol 创建写入数据的协议 MakeWriteProtocol(addr string, value int) []byte MakeWriteProtocolBits(bits int, addr string, ival int) []byte // ParseReadValue 解析从设备读取的值 ParseReadValue(data []byte) int ParseReadValueBits(data []byte) int // ParseWriteValue 解析写入设备的结果,并返回结果和是否成功 ParseWriteValue(data []byte) (int, bool) } const ( DeviceTypeMitsubishi = "mitsubishi" ) // LoadProtocol 根据配置加载不同的协议实现 func LoadProtocol(deviceType string) (SerialProtocol, error) { var protocol SerialProtocol switch deviceType { case DeviceTypeMitsubishi: protocol = &mitsubishi.ProtocolMitsubishi{} // 使用三菱设备协议 // case "OtherDevice": // protocol = &OtherDeviceProtocol{} // 使用其他设备的协议 default: return nil, fmt.Errorf("unsupported device type: %s", deviceType) } return protocol, nil } func ReadPLC(deviceType, url, label string, length int) (val int, err error) { protocol, err := LoadProtocol(deviceType) if err != nil { return 0, err } addr := protocol.ConvertLabelToAddress(label) var proto []byte if length == 1 { proto = protocol.MakeReadProtocol(addr) } else { proto = protocol.MakeReadProtocolBits(length*16, addr) } bp := base64.StdEncoding.EncodeToString(proto) fullUrl := fmt.Sprintf("%v?proto=%v", url, bp) resp, err := http.Get(fullUrl) if err != nil { logx.Errorf("ReadPLC http get failed:%v", err) return 0, err } body := readBody(resp.Body) data, err := base64.StdEncoding.DecodeString(string(body)) if err != nil { logx.Errorf("ReadPLC base64.StdEncoding.DecodeString failed: %v, data: %v", err, string(body)) return 0, err } if length == 1 { val = protocol.ParseReadValue(data) } else { val = protocol.ParseReadValueBits(data) } fmt.Println("read PLC:", val) return val, nil } func readBody(r io.ReadCloser) []byte { body, e := io.ReadAll(r) defer r.Close() if e != nil { fmt.Println("ReadAll resp body error:", e) return nil } return body } func WritePLC(deviceType, url, label string, val int) error { protocol, err := LoadProtocol(deviceType) if err != nil { return err } addr := protocol.ConvertLabelToAddress(label) var proto []byte if val < 65536 { proto = protocol.MakeWriteProtocol(addr, val) } else { proto = protocol.MakeWriteProtocolBits(32, addr, val) } bp := base64.StdEncoding.EncodeToString(proto) resp, err := http.Post(url, "text/plain", strings.NewReader(bp)) if err != nil { logx.Errorf("write plc http post failed:%v", err) return err } body := readBody(resp.Body) data, err := base64.StdEncoding.DecodeString(string(body)) if err != nil { logx.Errorf("write plc base64 StdEncoding DecodeString:%v", err) return err } val, ok := protocol.ParseWriteValue(data) if !ok { logx.Errorf("serial write failed, response:%v", val) return errors.New("failed") } logx.Errorf("serial write success, response:%v", val) return nil }