1.开场废话
第一次写文章有些紧张,那就先聊聊一些题外话,介绍我学习 Golang 的经历以及为什么选择使用这门语言进行机床数据采集。
作为一名 Java 开发者,我主要从事 CRUD 。但是,由于当前Java行业环境太卷了,大环境不好要求太高,我开始考虑学习其他编程语言(多一门技术准没错)。当时又看了golang的介绍什么高效性啊,静态类型啊。头一抬腚一撅 学!!! 刚好当时领导又让我搞某某学院机床数据采集项目。
当时在学习 Golang 的过程中,我发现相关视频教程和学习资料相对于 Java 来说较少,真的有时候看的我头大两圈(那时候我还没意识到真正的挑战还没开始)。后面跟着b站的学啊,把基础的全部学完之后我就开始上手写机床数据采集项目。
不看不知道一看吓一跳,我又接了个屎盆子,这个项目因为是之前人家写一半不写了丢给我们公司的。前端 jsp,然后点查询的时候调用对应的机床接口。 接口的源码没有人家不给,机床的采集命令没有,采集端口号不知道。然后我就想着没关系我去网上搜,搜毛毛搜到了都要钱。看到这有可能你们有些就想着我肯定花钱了,那恭喜你想错了。从猜端口号到猜采集指令都是我一个一个自己找的试的。 过程很折磨啊,几百次的尝试。开放端口号搜索我用的是nmap 感兴趣可以了解下。 这篇文章就是想着把我尝试出来的这些机床通过文章的方式告诉大家,希望对大家有些用!!!
2.海徳汉530机床数据采集
1.server:
其实我觉得海徳汉630用这个也是可以采集的,但是我没实验过,如果在做的看官有这个机型的可以尝试一下。
这个方法其实就是 通过net 这个包去建立Tcp 连接,然后发送采集指令,这个采集指令是我误打误撞试出来的,纯狗屎运。还有什么 GET 1001 感兴趣的可以去试一下。 然后机床接收到会返回你一整页html数据, cutOut()这个方法就是对这个html 进行截取,通过反射去添加到实体类里。
package Server
import (
"fmt"
"log"
"net"
"reflect"
"regexp"
"strconv"
"strings"
"time"
Dao "school/domain/entity/heidenhaintncdao"
)
// DataServer 端口号6880 Position-Display-Command-Data 是位置显示命令数据 可以进行数据采集
func DataServer() *Dao.HeiDenHain530 {
// 连接海德汉iTNC 530
conn, err := net.Dial("tcp", "127.0.0.1:6880")
if err != nil {
log.Fatal(err)
}
defer conn.Close()
// 发送数据采集请求
_, err = conn.Write([]byte("GET /1003\r\n"))
if err != nil {
fmt.Println("发送指令失败:", err)
//return "发送指令失败"
}
// 创建切片用来存储数据
buffer := make([]byte, 1024001)
conn.SetReadDeadline(time.Now().Add(10 * time.Second)) // 设置读取超时时间为10秒
n, err := conn.Read(buffer)
if err != nil {
if netErr, ok := err.(net.Error); ok && netErr.Timeout() {
fmt.Println("读取超时")
//return "读取超时"
} else {
fmt.Println("读取失败:", err)
//return "读取失败"
}
}
response := string(buffer[:n])
fmt.Println("读取到的数据" + response)
re := regexp.MustCompile(`(?s)<HTML>.*?</HTML>`)
match := re.FindString(response)
if match == "" {
fmt.Println("截取《HTML》内容失败")
}
data := cutOut(match)
// 写入数据库
//newDB, err := db.NewDB()
//
//if err != nil {
// fmt.Printf("连接失败,错误信息为%d", err)
//}
//newDB.Exec("insert ")
// 打印渲染完成后的HTML结果
fmt.Println(data)
fmt.Println("数据采集结果:", response)
转为字符串输出
//marshal, err := json.Marshal(data)
//if err != nil {
// fmt.Println("转换为字符串失败")
//
// //return "转换为字符串失败"
//}
return data
}
func cutOut(dataString string) *Dao.HeiDenHain530 {
parts := strings.Split(dataString, "<BR>")
data := Dao.HeiDenHain530{}
for i := 0; i < len(parts); i++ {
parts[i] = strings.ReplaceAll(parts[i], "\r", "")
parts[i] = strings.ReplaceAll(parts[i], "\n", "")
parts[i] = strings.ReplaceAll(parts[i], "[]", "")
split := strings.Split(parts[i], " = ")
if len(split) == 2 {
// 获取结构体类型的反射信息
name := reflect.TypeOf(data)
value := reflect.ValueOf(&data).Elem() // 获取结构体指针的可修改值
// 遍历所有字段并输出字段名
for i := 0; i < name.NumField(); i++ {
field := name.Field(i)
fieldName := field.Name
if strings.EqualFold(split[0], fieldName) {
fieldValue := value.Field(i)
if fieldValue.IsValid() {
if strings.EqualFold(split[0], "Pos1") || strings.EqualFold(split[0], "Pos2") ||
strings.EqualFold(split[0], "Dest") ||
strings.EqualFold(split[0], "Range") || strings.EqualFold(split[0], "Wtg3dXYZ") {
i2 := strings.Split(split[1], " ")
Pos1 := make([]int, len(i2))
for index, values := range i2 {
var value int
fmt.Sscanf(values, "%d", &value)
Pos1[index] = value
}
fieldValue.Set(reflect.ValueOf(Pos1))
} else {
switch fieldValue.Kind() {
case reflect.String:
fieldValue.SetString(split[1])
break
case reflect.Int:
var intValue int
fmt.Sscanf(split[1], "%d", &intValue)
fieldValue.SetInt(int64(intValue))
break
case reflect.Bool:
boolValue, _ := strconv.ParseBool(split[1])
fieldValue.SetBool(boolValue)
break
}
}
}
}
}
}
}
return &data
}
2.实体类:
package Dao
type HeiDenHain530 struct {
Statusidx int //状态指标索引
Manual int //手动模式指示
Tid string //线程 ID
Event string //事件标识
Pos1, Pos2, Dest, Range []int //pos1各个位置信息。
Wtg3dXYZ []int //三维 XYZ 值,包含了3个整数值,分别表示 x、y、z 坐标。
ToolAxisDisplacement int //工具轴位移
ToolAxisDisplacementMax int //工具轴最大位移
Rpm int //转速
RpmNoml int //标称转速
Feed string //进给速度
FeedNoml string //标称进给速度
Axis_i string // I 轴标识
Axis_h string //: H 轴标识
Axis_c string //C 轴标识
SpindleNumber int //主轴编号
Code_s int //代码 S
Code_f int //代码 F
Code_t int // 代码 T
M345 int //未知 M345
M789 int //未知
Safety_f int //安全标志 F
Safety_s int //安全标志 S
Safety_t int //安全标志 T
}
3.collenct
这就是转为json 数据返回给前端, main.go 的我就不展示了。
package collenct
import (
"encoding/json"
"fmt"
"log"
"net/http"
Dao "school/domain/entity/heidenhaintncdao"
Server "school/servers/heidenhaintns530"
)
type Message struct {
Text *Dao.HeiDenHain530 `json:"text"`
}
func Heidenhain(controller http.ResponseWriter, r *http.Request) {
// 创建一个消息对象
dto := Server.DataServer()
message := Message{Text: dto}
fmt.Println(message)
// 将消息对象转换为 JSON 格式
response, err := json.Marshal(message)
if err != nil {
log.Fatal(err)
}
// 设置响应头部信息
controller.Header().Set("Content-Type", "application/json")
controller.WriteHeader(http.StatusOK)
// 将 JSON 响应发送给客户端
_, err = controller.Write(response)
if err != nil {
log.Fatal(err)
}
}
4.注意事项
没有注意事项
3.发那科MD机床数据采集
其实我也不知道是不是MD还是TD ,但是都能读取到数据,这个就是根据Focas 1/2 包,一定要看注意事项!一定要看注意事项!一定要看注意事项!
1. server:
package fanake
/*
#cgo LDFLAGS: -L "D:/go/goproject/src/awesomeProject2/fanake" -l Fwlib64
#include <D:\\go\\goproject\\src\\awesomeProject2\\Fwlib32.h>
*/
import "C"
import (
"fmt"
"unsafe"
)
type Client struct {
Address string
Port int
handle C.ushort
Timeout int32
Connected bool
}
func NewClient(address string, port int, timeout int32) Client {
client := Client{}
client.Address = address
client.Connected = false
client.Port = port
client.Timeout = timeout
return client
}
func (client *Client) Connect() (err error) {
ret := C.cnc_allclibhndl3(C.CString(client.Address), C.ushort(client.Port), C.long(client.Timeout), &client.handle)
if ret == 0 {
client.Connected = true
} else {
return fmt.Errorf("Connect Error")
}
return nil
}
func (client *Client) DisConnect() (err error) {
C.cnc_freelibhndl(client.handle)
client.Connected = false
return nil
}
// CncProducts 主轴速度
func (client *Client) Rdparam() (ret C.short, result int32) {
obj := C.IODBPSD{}
ret = C.cnc_rdparam(client.handle, 6712, 0, 4+C.MAX_AXIS, &obj)
u := unsafe.Pointer(uintptr(unsafe.Pointer(&obj)) + 4)
ldata := *(*int32)(u)
return ret, ldata
}
}
目前就采集到主轴转速,其他的还在看Focas 1/2 那个文档等我看完写完会更新。
2.collenct:
这个要你们动手写一下方法把这串代码放进去了。
client := fanake.Client{Address: "127.0.0.1", Port: 8193, Timeout: 10000, Connected: true}
err := client.Connect()
if err != nil {
fmt.Println(err)
}
cncProducts, result := client.CncProducts()
if result == 0 {
fmt.Println(cncProducts)
} else {
fmt.Println("产品读取失败")
}
4.注意事项:
Focas 1/2 这个包怎么去找,github 上面 有一个人发的有,包括focas 1/2 文档。如果没有找不到私信我!!!我是懒的弄百度网盘啥的,私信我只是把GitHub地址给你。
如何在go上面写c语言代码这个就在CSDN搜一下就行,配置一下。
go的版本要和你的 Fwlib.dll的版本一样 ,要不然会报错 版本不兼容。
#cgo LDFLAGS: -L "D:/go/goproject/src/awesomeProject2/fanake" -l Fwlib64 这个就是我的fwlib.dll 的路径是在"D:/go/goproject/src/awesomeProject2/fanake" ,去找的版本是Fwlib64
#include <D:\\go\\goproject\\src\\awesomeProject2\\Fwlib32.h> 这个 库就直接把你路径搞上去就行 这个32位的我64位的可以用,我就没管。