Skip to content
ย 
ย 

Folders and files

NameName
Last commit message
Last commit date

Latest commit

ย 

History

108 Commits
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 

Repository files navigation

Ethernet/IP Go ๅบ“

Go ่ฏญ่จ€ๅฎž็Žฐ็š„ Ethernet/IP ๅ่ฎฎๅบ“๏ผŒๆ”ฏๆŒไธŽ Allen-Bradley PLC ็ญ‰่ฎพๅค‡่ฟ›่กŒ้€šไฟกใ€‚

็›ฎๅฝ•


ๅŠŸ่ƒฝ็‰นๆ€ง

  • โœ… TCP ่ฟžๆŽฅ็ฎก็†๏ผˆไผš่ฏๆณจๅ†Œ/ๆณจ้”€๏ผ‰
  • โœ… UDP ่ฟžๆŽฅๆ”ฏๆŒ
  • โœ… UDP ่ฎพๅค‡ๅ‘็Žฐ๏ผˆBroadcast Discovery๏ผ‰
  • โœ… Tag ่ฏปๅ†™ๆ“ไฝœ
  • โœ… ๆ‰น้‡ Tag ๆ“ไฝœ๏ผˆTagGroup๏ผ‰
  • โœ… ่ฟžๆŽฅๆฑ ๆ”ฏๆŒ๏ผˆEIPTCPPool๏ผ‰
  • โœ… UDT๏ผˆ็”จๆˆทๅฎšไน‰็ฑปๅž‹๏ผ‰ๆ”ฏๆŒ
  • โœ… ็ฌฆๅทๅฏปๅ€๏ผˆSymbolic Addressing๏ผ‰
  • โœ… ่ฟžๆŽฅๅ‰ๅ‘ๅผ€ๆ”พ๏ผˆForward Open๏ผ‰/ๅ…ณ้—ญ๏ผˆForward Close๏ผ‰
  • โœ… ๆถˆๆฏ่ทฏ็”ฑๅ™จ๏ผˆMessage Router๏ผ‰
  • โœ… ็ผ“ๅ†ฒๅŒบๆฑ ๅŒ–ไผ˜ๅŒ–
  • โœ… ็บฟ็จ‹ๅฎ‰ๅ…จ่ฎพ่ฎก
  • โœ… ๅ…ผๅฎน๏ผˆLogix Class 2 ๅฏน่ฑกๆ ‡็ญพ่ฎฟ้—ฎ๏ผ‰
  • โœ… ่ฟžๆŽฅ็Šถๆ€ๆฃ€ๆŸฅ๏ผˆIsConnected๏ผ‰
  • โœ… ่‡ชๅŠจ้‡่ฟžๆœบๅˆถ๏ผˆๆŒ‡ๆ•ฐ้€€้ฟ๏ผŒๆœ€ๅคš 3 ๆฌก้‡่ฏ•๏ผ‰
  • โœ… ๆ‰น้‡ๆ•ฐๆฎๅŒ…ๆ“ไฝœ๏ผˆBatchRead/BatchWrite๏ผ‰
  • โœ… ่ถ…ๆ—ถ้…็ฝฎ๏ผˆ่ฟžๆŽฅ่ถ…ๆ—ถใ€่ฏปๅ†™่ถ…ๆ—ถใ€ๆ“ไฝœ่ถ…ๆ—ถ๏ผ‰
  • โœ… ไบ‹ๅŠกๆ€งๆ”ฏๆŒ๏ผˆTagGroup ๅŽŸๅญๅ†™ๅ…ฅ๏ผ‰
  • โœ… ่ฏŠๆ–ญ็›‘ๆŽง๏ผˆ่ฟžๆŽฅ็Šถๆ€ใ€็ปŸ่ฎกๆŒ‡ๆ ‡ใ€ไบ‹ไปถ็›‘ๅฌ๏ผ‰
  • โœ… STRING ๅ†™ๅ…ฅไผ˜ๅŒ–๏ผˆๅ•ๆฌก่ฏทๆฑ‚ๅฎŒๆˆ๏ผ‰

ๅฟซ้€Ÿๅผ€ๅง‹

ๅฎ‰่ฃ…ๅบ“

ไฝฟ็”จๆœ€ๆ–ฐ็‰ˆๆœฌ

go get github.com/anviod/ethernet-ip@latest

ๆŒ‡ๅฎš็‰ˆๆœฌ

# ไฝฟ็”จ็‰นๅฎš็‰ˆๆœฌ๏ผˆๆŽจ่็”จไบŽ็”Ÿไบง็Žฏๅขƒ๏ผ‰
go get github.com/anviod/ethernet-ip@v0.0.4

# ไฝฟ็”จ็‰นๅฎšๆไบค
go get github.com/anviod/ethernet-ip@abc1234

# ไฝฟ็”จๅˆ†ๆ”ฏ
go get github.com/anviod/ethernet-ip@main

ๅฏผๅ…ฅๅบ“

import (
    "log"
    "github.com/anviod/ethernet-ip"
)

ๅˆ›ๅปบ่ฟžๆŽฅ

// ๅˆ›ๅปบ TCP ่ฟžๆŽฅๅฏน่ฑก
conn, err := ethernet_ip.NewTCP("192.168.1.10", nil)
if err != nil {
    log.Fatal("ๅˆ›ๅปบ่ฟžๆŽฅๅคฑ่ดฅ:", err)
}
defer conn.Close()

// ๅปบ็ซ‹่ฟžๆŽฅ
if err := conn.Connect(); err != nil {
    log.Fatal("่ฟžๆŽฅๅคฑ่ดฅ:", err)
}

่ฏปๅ– Tag

// ่Žทๅ–ๆ‰€ๆœ‰ Tag
tags, err := conn.AllTags()
if err != nil {
    log.Fatal("่Žทๅ– Tag ๅˆ—่กจๅคฑ่ดฅ:", err)
}

// ่ฏปๅ–ๅ•ไธช Tag
tag := tags["Counter"]
if err := tag.Read(); err != nil {
    log.Fatal("่ฏปๅ– Tag ๅคฑ่ดฅ:", err)
}

// ่Žทๅ–ๅ€ผ
log.Printf("Tag ๅ€ผ: %d\n", tag.Int32())

ๅ†™ๅ…ฅ Tag

// ่ฎพ็ฝฎๆ–ฐๅ€ผๅนถๅ†™ๅ…ฅ
tag.SetInt32(12345)
if err := tag.Write(); err != nil {
    log.Fatal("ๅ†™ๅ…ฅ Tag ๅคฑ่ดฅ:", err)
}
log.Println("ๅ†™ๅ…ฅๆˆๅŠŸ")

ๆ‰น้‡ๆ“ไฝœ

// ๅˆ›ๅปบ Tag ็ป„
lock := new(sync.Mutex)
group := ethernet_ip.NewTagGroup(lock)

// ๆทปๅŠ ๅคšไธช Tag
group.Add(tags["tag1"])
group.Add(tags["tag2"])

// ๆ‰น้‡่ฏปๅ–
if err := group.Read(); err != nil {
    log.Fatal("ๆ‰น้‡่ฏปๅ–ๅคฑ่ดฅ:", err)
}

// ๆ‰น้‡ๅ†™ๅ…ฅ
tags["tag1"].SetInt32(100)
tags["tag2"].SetString("updated")
if err := group.Write(); err != nil {
    log.Fatal("ๆ‰น้‡ๅ†™ๅ…ฅๅคฑ่ดฅ:", err)
}

ๆ”ฏๆŒ็š„ๆ•ฐๆฎ็ฑปๅž‹

็ฑปๅž‹ ่ฏดๆ˜Ž ็ฌฆๅท
BOOL ๅธƒๅฐ”ๅž‹ 0xc1
SINT / INT8 8ไฝๆœ‰็ฌฆๅทๆ•ดๆ•ฐ 0xc2
INT / INT16 16ไฝๆœ‰็ฌฆๅทๆ•ดๆ•ฐ 0xc3
DINT / INT32 32ไฝๆœ‰็ฌฆๅทๆ•ดๆ•ฐ 0xc4
LINT / INT64 64ไฝๆœ‰็ฌฆๅทๆ•ดๆ•ฐ 0xc5
USINT / UINT8 8ไฝๆ— ็ฌฆๅทๆ•ดๆ•ฐ 0xc6
UINT / UINT16 16ไฝๆ— ็ฌฆๅทๆ•ดๆ•ฐ 0xc7
UDINT / UINT32 32ไฝๆ— ็ฌฆๅทๆ•ดๆ•ฐ 0xc8
ULINT / UINT64 64ไฝๆ— ็ฌฆๅทๆ•ดๆ•ฐ 0xc9
REAL / FLOAT 32ไฝๆตฎ็‚นๆ•ฐ 0xca
LREAL / DOUBLE 64ไฝๆตฎ็‚นๆ•ฐ 0xcb
STRING ๅญ—็ฌฆไธฒ 0xfce

ๆžถๆž„่ฎพ่ฎก

ๆจกๅ—็ป“ๆž„

ethernet-ip/
โ”œโ”€โ”€ bufferx/              # ๅญ—่Š‚็ผ“ๅ†ฒๅŒบๆ“ไฝœ
โ”‚   โ”œโ”€โ”€ bufferx.go        # ๆ”ฏๆŒๅฐ็ซฏ/ๅคง็ซฏ่ฏปๅ†™ใ€็ผ“ๅ†ฒๅŒบๆฑ ๅŒ–
โ”‚   โ”œโ”€โ”€ bufferx_test.go   # ๅ•ๅ…ƒๆต‹่ฏ•
โ”‚   โ””โ”€โ”€ bufferx_benchmark_test.go # ๆ€ง่ƒฝๆต‹่ฏ•
โ”œโ”€โ”€ command/              # EIP ๅ‘ฝไปคๅฎšไน‰
โ”‚   โ”œโ”€โ”€ command.go        # ๅ‘ฝไปคๅธธ้‡๏ผˆๆณจๅ†Œไผš่ฏใ€ๅ‘้€ๆ•ฐๆฎ็ญ‰๏ผ‰
โ”‚   โ””โ”€โ”€ command_test.go   # ๅ•ๅ…ƒๆต‹่ฏ•
โ”œโ”€โ”€ messages/             # ๆถˆๆฏๅค„็†
โ”‚   โ”œโ”€โ”€ packet/          # ๆ•ฐๆฎๅŒ…็ผ–่งฃ็ 
โ”‚   โ”‚   โ”œโ”€โ”€ packet.go    # ๆ•ฐๆฎๅŒ…็ป“ๆž„
โ”‚   โ”‚   โ”œโ”€โ”€ commonPacketFormat.go # CPF ๆ ผๅผ
โ”‚   โ”‚   โ”œโ”€โ”€ messageRouter.go      # ๆถˆๆฏ่ทฏ็”ฑๅ™จ
โ”‚   โ”‚   โ”œโ”€โ”€ services.go  # ๆœๅŠกๅฎšไน‰
โ”‚   โ”‚   โ”œโ”€โ”€ cmm.go       # CIP ๆถˆๆฏ็ฎก็†
โ”‚   โ”‚   โ”œโ”€โ”€ ucmm.go      # ้ž่ฟžๆŽฅๆถˆๆฏ็ฎก็†
โ”‚   โ”‚   โ”œโ”€โ”€ data.go      # ๆ•ฐๆฎ้กนๅค„็†
โ”‚   โ”‚   โ”œโ”€โ”€ utils.go     # ๅทฅๅ…ทๅ‡ฝๆ•ฐ
โ”‚   โ”‚   โ””โ”€โ”€ packet_test.go # ๅ•ๅ…ƒๆต‹่ฏ•
โ”‚   โ”œโ”€โ”€ registerSession/  # ไผš่ฏๆณจๅ†Œ
โ”‚   โ”œโ”€โ”€ unRegisterSession/ # ไผš่ฏๆณจ้”€
โ”‚   โ”œโ”€โ”€ listIdentity/     # ่ฎพๅค‡่ฏ†ๅˆซไฟกๆฏ
โ”‚   โ”œโ”€โ”€ listInterface/    # ๆŽฅๅฃๅˆ—่กจ
โ”‚   โ”œโ”€โ”€ listServices/     # ๆœๅŠกๅˆ—่กจ
โ”‚   โ”œโ”€โ”€ sendRRData/       # ๅ‘้€่ทฏ็”ฑๆ•ฐๆฎ
โ”‚   โ”œโ”€โ”€ sendUnitData/     # ๅ‘้€ๅ•ๅ…ƒๆ•ฐๆฎ
โ”‚   โ””โ”€โ”€ nop/              # NOP ๅ‘ฝไปค๏ผˆ็ฉบๆ“ไฝœ๏ผ‰
โ”œโ”€โ”€ path/                # CIP ่ทฏๅพ„ๆž„ๅปบ
โ”‚   โ”œโ”€โ”€ path.go          # ้€ป่พ‘่ทฏๅพ„ใ€็ซฏๅฃ่ทฏๅพ„ใ€ๆ•ฐๆฎ่ทฏๅพ„
โ”‚   โ””โ”€โ”€ path_test.go     # ๅ•ๅ…ƒๆต‹่ฏ•
โ”œโ”€โ”€ types/               # ็ฑปๅž‹ๅฎšไน‰
โ”‚   โ””โ”€โ”€ types.go         # ๆ‰€ๆœ‰ๆ•ฐๆฎ็ฑปๅž‹ๅฎšไน‰
โ”œโ”€โ”€ utils/               # ๅทฅๅ…ทๅ‡ฝๆ•ฐ
โ”‚   โ”œโ”€โ”€ len.go           # ้•ฟๅบฆ่ฎก็ฎ—
โ”‚   โ”œโ”€โ”€ mmap.go          # ๅ†…ๅญ˜ๆ˜ ๅฐ„
โ”‚   โ”œโ”€โ”€ mmap_unix.go     # Unix ๅนณๅฐๅ†…ๅญ˜ๆ˜ ๅฐ„
โ”‚   โ”œโ”€โ”€ mmap_windows.go  # Windows ๅนณๅฐๅ†…ๅญ˜ๆ˜ ๅฐ„
โ”‚   โ””โ”€โ”€ simd.go          # SIMD ไผ˜ๅŒ–
โ”œโ”€โ”€ test/                # ้›†ๆˆๆต‹่ฏ•
โ”‚   โ”œโ”€โ”€ cpppo/           # cpppo ๅ…ผๅฎนๆ€งๆต‹่ฏ•
โ”‚   โ”œโ”€โ”€ protocol_verifier_test.go # ๅ่ฎฎ้ชŒ่ฏๆต‹่ฏ•
โ”‚   โ”œโ”€โ”€ access_mode_test.go # ่ฎฟ้—ฎๆจกๅผๆต‹่ฏ•
โ”‚   โ”œโ”€โ”€ concurrency_test.go # ๅนถๅ‘ๆต‹่ฏ•
โ”‚   โ””โ”€โ”€ benchmark_test.go # ๆ€ง่ƒฝๅŸบๅ‡†ๆต‹่ฏ•
โ”œโ”€โ”€ doc/                 # ๆ–‡ๆกฃ
โ”‚   โ”œโ”€โ”€ PERFORMANCE_OPTIMIZATION*.md # ๆ€ง่ƒฝไผ˜ๅŒ–ๆ–‡ๆกฃ
โ”‚   โ”œโ”€โ”€ performance_report.json      # ๆ€ง่ƒฝๆŠฅๅ‘Š
โ”‚   โ””โ”€โ”€ 2026ๅนด5ๆœˆ25ๆ—ฅ.md # ๅ…ผๅฎนๆ€งๅฎกๆŸฅๆŠฅๅ‘Š
โ”œโ”€โ”€ config.go            # ้…็ฝฎ็ป“ๆž„๏ผˆๅซ่ถ…ๆ—ถ้…็ฝฎ๏ผ‰
โ”œโ”€โ”€ context.go           # ไธŠไธ‹ๆ–‡็”Ÿๆˆๅ™จ
โ”œโ”€โ”€ doc.go               # Go ๆ–‡ๆกฃๆณจ้‡Š
โ”œโ”€โ”€ tcp.go               # TCP ่ฟžๆŽฅ็ฎก็†๏ผˆๅซ้‡่ฟžๆœบๅˆถใ€็›‘ๆŽง้›†ๆˆ๏ผ‰
โ”œโ”€โ”€ tcp_pool.go          # TCP ่ฟžๆŽฅๆฑ 
โ”œโ”€โ”€ tag.go               # Tag ๆ“ไฝœๆ ธๅฟƒ๏ผˆๅซไบ‹ๅŠกๆ€งๆ”ฏๆŒ๏ผ‰
โ”œโ”€โ”€ request.go           # ่ฏทๆฑ‚ๅค„็†๏ผˆๅซ ForwardClose๏ผ‰
โ”œโ”€โ”€ monitoring.go        # ่ฏŠๆ–ญ็›‘ๆŽง๏ผˆ่ฟžๆŽฅ็Šถๆ€ใ€็ปŸ่ฎกๆŒ‡ๆ ‡๏ผ‰
โ”œโ”€โ”€ udp.go               # UDP ๅนฟๆ’ญๅ‘็Žฐ
โ”œโ”€โ”€ example.go           # ไฝฟ็”จ็คบไพ‹
โ””โ”€โ”€ go.mod               # Go ๆจกๅ—้…็ฝฎ

ๆ ธๅฟƒ็ป„ไปถ

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚                    EIPTCP                                โ”‚
โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”              โ”‚
โ”‚  โ”‚  Config  โ”‚  โ”‚ TCPConn  โ”‚  โ”‚ Session  โ”‚              โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜              โ”‚
โ”‚                                                         โ”‚
โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”           โ”‚
โ”‚  โ”‚              Request Handler              โ”‚           โ”‚
โ”‚  โ”‚  - RegisterSession()                      โ”‚           โ”‚
โ”‚  โ”‚  - UnRegisterSession()                    โ”‚           โ”‚
โ”‚  โ”‚  - ListIdentity()                         โ”‚           โ”‚
โ”‚  โ”‚  - SendRRData() / SendUnitData()         โ”‚           โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜           โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                          โ”‚
                          โ–ผ
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚                      Tag                                โ”‚
โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”              โ”‚
โ”‚  โ”‚   Name   โ”‚  โ”‚   Type   โ”‚  โ”‚  Value   โ”‚              โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜              โ”‚
โ”‚                                                         โ”‚
โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”           โ”‚
โ”‚  โ”‚           Read/Write Methods              โ”‚           โ”‚
โ”‚  โ”‚  - Read() / Write()                      โ”‚           โ”‚
โ”‚  โ”‚  - SetInt32() / SetString()              โ”‚           โ”‚
โ”‚  โ”‚  - Int32() / String()                    โ”‚           โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜           โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

่ฐƒ็”จๆต็จ‹

1. ่ฟžๆŽฅๅปบ็ซ‹ๆต็จ‹

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚   ๅผ€ๅง‹      โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”˜
       โ”‚
       โ–ผ
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  NewTCP()       โ”‚  ๅˆ›ๅปบ TCP ่ฟžๆŽฅๅฏน่ฑก
โ”‚  - ่งฃๆžๅœฐๅ€     โ”‚
โ”‚  - ๅˆๅง‹ๅŒ–้…็ฝฎ   โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
       โ”‚
       โ–ผ
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  Connect()      โ”‚  ๅปบ็ซ‹ TCP ่ฟžๆŽฅ
โ”‚  - DialTCP()    โ”‚
โ”‚  - SetKeepAlive โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
       โ”‚
       โ–ผ
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚ RegisterSession โ”‚  ๆณจๅ†Œไผš่ฏ
โ”‚  - ็”ŸๆˆไธŠไธ‹ๆ–‡   โ”‚
โ”‚  - ๅ‘้€ๆณจๅ†Œ่ฏทๆฑ‚ โ”‚
โ”‚  - ่Žทๅ–ไผš่ฏๅฅๆŸ„ โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
       โ”‚
       โ–ผ
   โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
   โ”‚ๅฎŒๆˆ   โ”‚
   โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

2. Tag ่ฏปๅ–ๆต็จ‹

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚   ๅผ€ๅง‹      โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”˜
       โ”‚
       โ–ผ
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚ AllTags()       โ”‚  ่Žทๅ–ๆ‰€ๆœ‰ Tag
โ”‚  - ๆŸฅ่ฏขๅฎžไพ‹ๅฑžๆ€ง  โ”‚
โ”‚  - ๆž„ๅปบ Tag ๆ˜ ๅฐ„ โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
       โ”‚
       โ–ผ
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  tag.Read()     โ”‚  ่ฏปๅ–ๅ•ไธช Tag
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
       โ”‚
       โ–ผ
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  TCP.Send()             โ”‚  ๅ‘้€่ฏทๆฑ‚
โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”โ”‚
โ”‚  โ”‚ readRequest()       โ”‚โ”‚  ๆž„ๅปบ่ฏป่ฏทๆฑ‚
โ”‚  โ”‚ - ๆž„ๅปบ่ทฏๅพ„          โ”‚โ”‚
โ”‚  โ”‚ - ็ผ–็ ๆœๅŠก่ฏทๆฑ‚      โ”‚โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
       โ”‚
       โ–ผ
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  TCP.SendRRData()       โ”‚  ๅ‘้€่ทฏ็”ฑๆ•ฐๆฎ
โ”‚  - ๅฐ่ฃ…ไธบ CPF ๅŒ…        โ”‚
โ”‚  - ๅ‘้€ๅˆฐ PLC           โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
       โ”‚
       โ–ผ
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  ๆŽฅๆ”ถๅ“ๅบ”                โ”‚
โ”‚  - ่งฃ็  MessageRouter   โ”‚
โ”‚  - ่งฃๆžๅ“ๅบ”ๆ•ฐๆฎ         โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
       โ”‚
       โ–ผ
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  readParser()           โ”‚  ่งฃๆž่ฏปๅ–็ป“ๆžœ
โ”‚  - ๆๅ–็ฑปๅž‹            โ”‚
โ”‚  - ๆๅ–ๅ€ผ              โ”‚
โ”‚  - ๆ›ดๆ–ฐ Tag.value      โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
       โ”‚
       โ–ผ
   โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
   โ”‚ๅฎŒๆˆ   โ”‚
   โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

3. Tag ๅ†™ๅ…ฅๆต็จ‹

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚   ๅผ€ๅง‹      โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”˜
       โ”‚
       โ–ผ
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚ tag.SetInt32()  โ”‚  ่ฎพ็ฝฎๅ€ผ๏ผˆๆˆ–ๅ…ถไป– Set ๆ–นๆณ•๏ผ‰
โ”‚ - ๅบๅˆ—ๅŒ–ๅ€ผ      โ”‚
โ”‚ - ๅญ˜ๅ…ฅ wValue  โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
       โ”‚
       โ–ผ
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  tag.Write()    โ”‚  ๆ‰ง่กŒๅ†™ๅ…ฅ
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
       โ”‚
       โ–ผ
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  writeRequest()         โ”‚  ๆž„ๅปบๅ†™่ฏทๆฑ‚
โ”‚  - ็ผ–็ ็ฑปๅž‹            โ”‚
โ”‚  - ็ผ–็ ๅ€ผ              โ”‚
โ”‚  - ๆž„ๅปบ่ทฏๅพ„            โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
       โ”‚
       โ–ผ
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  TCP.Send()             โ”‚  ๅ‘้€ๅ†™ๅ…ฅ่ฏทๆฑ‚
โ”‚  - multiple() ๅฐ่ฃ…      โ”‚
โ”‚  - SendRRData/SendUnit โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
       โ”‚
       โ–ผ
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  ๆŽฅๆ”ถๅ“ๅบ”                โ”‚
โ”‚  - ๆฃ€ๆŸฅ็Šถๆ€             โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
       โ”‚
       โ–ผ
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  ๆ›ดๆ–ฐ Tag.value         โ”‚
โ”‚  - copy(wValue, value) โ”‚
โ”‚  - ๆธ…็ฉบ wValue         โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
       โ”‚
       โ–ผ
   โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
   โ”‚ๅฎŒๆˆ   โ”‚
   โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

4. ่ฟžๆŽฅๆฑ ไฝฟ็”จๆต็จ‹

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚   ๅผ€ๅง‹      โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”˜
       โ”‚
       โ–ผ
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚ NewTCPPool()    โ”‚  ๅˆ›ๅปบ่ฟžๆŽฅๆฑ 
โ”‚ - ๆŒ‡ๅฎšๅฎน้‡     โ”‚
โ”‚ - ๅˆๅง‹ๅŒ–่ฟžๆŽฅ   โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
       โ”‚
       โ–ผ
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚ Get()           โ”‚  ่Žทๅ–่ฟžๆŽฅ
โ”‚ - ไปŽๆฑ ไธญ่Žทๅ–   โ”‚
โ”‚ - ้˜ปๅกž็ญ‰ๅพ…     โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
       โ”‚
       โ–ผ
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  ไฝฟ็”จ่ฟžๆŽฅ      โ”‚
โ”‚ - Tag ่ฏปๅ†™    โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
       โ”‚
       โ–ผ
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚ Put()           โ”‚  ๅฝ’่ฟ˜่ฟžๆŽฅ
โ”‚ - ๆ”พๅ›žๆฑ ไธญ     โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
       โ”‚
       โ–ผ
   โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
   โ”‚ๅฎŒๆˆ   โ”‚
   โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

้…็ฝฎ้€‰้กน

Config ็ป“ๆž„

type Config struct {
    TCPPort          uint16        // TCP ็ซฏๅฃ๏ผˆ้ป˜่ฎค 44818๏ผ‰
    UDPPort          uint16        // UDP ็ซฏๅฃ๏ผˆ้ป˜่ฎค 44818๏ผ‰
    Slot             uint8         // ๆŽงๅˆถๅ™จๆงฝไฝ
    TimeTick         types.USInt   // ่ฟžๆŽฅๆ—ถ้—ดๅˆปๅบฆ๏ผˆๆฏซ็ง’๏ผ‰
    TimeTickOut      types.USInt   // ่ฟžๆŽฅ่ถ…ๆ—ถ๏ผˆTimeTick ๅ•ไฝ๏ผ‰
    ConnectTimeout   time.Duration // ่ฟžๆŽฅ่ถ…ๆ—ถ๏ผˆ้ป˜่ฎค 30 ็ง’๏ผ‰
    ReadTimeout      time.Duration // ่ฏปๅ–่ถ…ๆ—ถ๏ผˆ้ป˜่ฎค 10 ็ง’๏ผ‰
    WriteTimeout     time.Duration // ๅ†™ๅ…ฅ่ถ…ๆ—ถ๏ผˆ้ป˜่ฎค 10 ็ง’๏ผ‰
    OperationTimeout time.Duration // ๆ“ไฝœ่ถ…ๆ—ถ๏ผˆ้ป˜่ฎค 15 ็ง’๏ผ‰
}

ไฝฟ็”จ็คบไพ‹

config := ethernet_ip.DefaultConfig()
config.ConnectTimeout = time.Second * 60  // ่‡ชๅฎšไน‰่ฟžๆŽฅ่ถ…ๆ—ถ
config.ReadTimeout = time.Second * 15     // ่‡ชๅฎšไน‰่ฏปๅ–่ถ…ๆ—ถ

conn, err := ethernet_ip.NewTCP("192.168.1.10", config)

่ฏŠๆ–ญ็›‘ๆŽง

่ฟžๆŽฅ็Šถๆ€

state := conn.GetConnectionState()
fmt.Printf("ๅฝ“ๅ‰็Šถๆ€: %s\n", state.String())
// ็Šถๆ€ๅ€ผ: Disconnected, Connecting, Connected, Reconnecting

็ปŸ่ฎกๆŒ‡ๆ ‡

stats := conn.GetConnectionStats()
fmt.Printf("่ฟžๆŽฅๆฌกๆ•ฐ: %d\n", stats.ConnectCount)
fmt.Printf("ๆ–ญๅผ€ๆฌกๆ•ฐ: %d\n", stats.DisconnectCount)
fmt.Printf("้‡่ฟžๆฌกๆ•ฐ: %d\n", stats.ReconnectCount)
fmt.Printf("ๆ€ป่ฏทๆฑ‚ๆ•ฐ: %d\n", stats.TotalRequests)
fmt.Printf("ๆˆๅŠŸ่ฏทๆฑ‚ๆ•ฐ: %d\n", stats.SuccessfulRequests)
fmt.Printf("ๅคฑ่ดฅ่ฏทๆฑ‚ๆ•ฐ: %d\n", stats.FailedRequests)
fmt.Printf("ๅนณๅ‡ๅ“ๅบ”ๆ—ถ้—ด: %v\n", stats.AvgResponseTime)
fmt.Printf("ๆœ€ๅฐๅ“ๅบ”ๆ—ถ้—ด: %v\n", stats.MinResponseTime)
fmt.Printf("ๆœ€ๅคงๅ“ๅบ”ๆ—ถ้—ด: %v\n", stats.MaxResponseTime)
fmt.Printf("ๆ€ปๅ‘้€ๅญ—่Š‚: %d\n", stats.TotalBytesSent)
fmt.Printf("ๆ€ปๆŽฅๆ”ถๅญ—่Š‚: %d\n", stats.TotalBytesReceived)

ไบ‹ไปถ็›‘ๅฌ

// ๆณจๅ†Œ่ฟžๆŽฅ็Šถๆ€็›‘ๅฌๅ™จ
listener := func(event ethernet_ip.ConnectionEvent) {
    fmt.Printf("[%s] ็Šถๆ€ๅ˜ๅŒ–: %s\n", 
        event.Timestamp.Format(time.RFC3339),
        event.State.String())
    
    if event.Error != nil {
        fmt.Printf("้”™่ฏฏ: %v\n", event.Error)
    }
    
    // ๅฏไปฅๅœจ่ฟ™้‡Œๅฎž็Žฐๅ‘Š่ญฆใ€ๆ—ฅๅฟ—่ฎฐๅฝ•็ญ‰้€ป่พ‘
    if event.State == ethernet_ip.StateDisconnected {
        log.Warn("่ฟžๆŽฅๆ–ญๅผ€๏ผŒๅ‡†ๅค‡้‡่ฟž...")
    } else if event.State == ethernet_ip.StateConnected {
        log.Info("่ฟžๆŽฅๅทฒๆขๅค")
    }
}

conn.AddConnectionListener(listener)

// ็งป้™ค็›‘ๅฌๅ™จ
conn.RemoveConnectionListener(listener)

้‡็ฝฎ็ปŸ่ฎก

conn.ResetConnectionStats()

UDP ่ฎพๅค‡ๅ‘็Žฐ

ๅŸบๆœฌ็”จๆณ•

// ๅ‘็Žฐ็ฝ‘็ปœไธญ็š„ EIP ่ฎพๅค‡๏ผˆ้ป˜่ฎค่ถ…ๆ—ถ 5 ็ง’๏ผ‰
devices, err := ethernet_ip.DiscoverDevices(time.Second * 5)
if err != nil {
    log.Fatal(err)
}

for _, device := range devices {
    fmt.Printf("่ฎพๅค‡ๅ็งฐ: %s\n", device.DeviceName)
    fmt.Printf("IPๅœฐๅ€: %s\n", device.IPAddress)
    fmt.Printf("MACๅœฐๅ€: %s\n", device.MacAddress)
    fmt.Printf("ไบงๅ“ไปฃ็ : %d\n", device.ProductCode)
    fmt.Printf("ๅบๅˆ—ๅท: %d\n", device.SerialNumber)
    fmt.Printf("ไพ›ๅบ”ๅ•†ID: %d\n", device.VendorID)
    fmt.Printf("็‰ˆๆœฌ: %d.%d\n", device.RevisionMajor, device.RevisionMinor)
    fmt.Println("---")
}

ๆŒ‡ๅฎš็ซฏๅฃ

// ไฝฟ็”จ่‡ชๅฎšไน‰็ซฏๅฃ่ฟ›่กŒ่ฎพๅค‡ๅ‘็Žฐ
devices, err := ethernet_ip.DiscoverDevicesWithPort(time.Second * 5, 44818)

DiscoveredDevice ็ป“ๆž„

ๅญ—ๆฎต ็ฑปๅž‹ ่ฏดๆ˜Ž
IPAddress net.IP ่ฎพๅค‡ IP ๅœฐๅ€
MacAddress string MAC ๅœฐๅ€
DeviceName string ่ฎพๅค‡ๅ็งฐ
ProductCode uint16 ไบงๅ“ไปฃ็ 
RevisionMajor uint8 ไธป็‰ˆๆœฌๅท
RevisionMinor uint8 ๆฌก็‰ˆๆœฌๅท
Status uint16 ่ฎพๅค‡็Šถๆ€
SerialNumber uint32 ๅบๅˆ—ๅท
VendorID uint16 ไพ›ๅบ”ๅ•† ID

ไบ‹ๅŠกๆ€งๆ”ฏๆŒ

TagGroup ๅŽŸๅญๅ†™ๅ…ฅ

// ๅˆ›ๅปบ TagGroup ๅนถๅฏ็”จไบ‹ๅŠกๆ€ง
group := ethernet_ip.NewTagGroup(nil)
group.AtomicWrite = true  // ๅฏ็”จๅŽŸๅญๅ†™ๅ…ฅๆจกๅผ

// ๆทปๅŠ ๅคšไธช Tag
group.Add(tag1)
group.Add(tag2)
group.Add(tag3)

// ่ฎพ็ฝฎๆ–ฐๅ€ผ
tag1.SetInt32(100)
tag2.SetString("test")
tag3.SetFloat32(3.14)

// ๆ‰ง่กŒๆ‰น้‡ๅ†™ๅ…ฅ๏ผˆไบ‹ๅŠกๆ€ง๏ผ‰
err := group.Write()
if err != nil {
    // ๅฆ‚ๆžœไปปไฝ•ไธ€ไธชๅ†™ๅ…ฅๅคฑ่ดฅ๏ผŒๆ‰€ๆœ‰ๆ ‡็ญพ็š„ changed ็Šถๆ€้ƒฝไผšๆขๅค
    log.Printf("ๅ†™ๅ…ฅๅคฑ่ดฅ๏ผŒๅทฒๅ›žๆปš: %v\n", err)
} else {
    log.Println("ๆ‰€ๆœ‰ๆ ‡็ญพๅ†™ๅ…ฅๆˆๅŠŸ")
}

ไบ‹ๅŠกๆ€งๅŽŸ็†

ๅฝ“ AtomicWrite ๅฏ็”จๆ—ถ๏ผŒTagGroup.Write() ไผš๏ผš

  1. ๆ”ถ้›†ๆ‰€ๆœ‰ๅพ…ๅ†™ๅ…ฅ็š„ๆ ‡็ญพ
  2. ๅ‘้€ๆ‰น้‡ๅ†™ๅ…ฅ่ฏทๆฑ‚
  3. ๆฃ€ๆŸฅๆฏไธชๅ“ๅบ”็š„็Šถๆ€็ 
  4. ๅฆ‚ๆžœไปปไฝ•ไธ€ไธชๅ†™ๅ…ฅๅคฑ่ดฅ๏ผŒๆขๅคๆ‰€ๆœ‰ๆ ‡็ญพ็š„ changed ็Šถๆ€
  5. ๅชๆœ‰ๆ‰€ๆœ‰ๅ†™ๅ…ฅ้ƒฝๆˆๅŠŸ๏ผŒๆ‰ๆ›ดๆ–ฐๆ ‡็ญพ็š„ๅ€ผ

่ฎฟ้—ฎๆจกๅผ

ๆœฌๅบ“ๆ”ฏๆŒไธค็ง่ฎฟ้—ฎๆจกๅผๆฅไธŽ PLC ้€šไฟก๏ผšๆ ‡ๅ‡† CIP ๆจกๅผๅ’Œ Logix ๆจกๅผใ€‚

ๆ ‡ๅ‡† CIP ๆจกๅผ

ๆ ‡ๅ‡† CIP๏ผˆCommon Industrial Protocol๏ผ‰ๆจกๅผไฝฟ็”จ Symbolic Addressing ๆ–นๅผ่ฎฟ้—ฎๆ ‡็ญพใ€‚่ฟ™็งๆจกๅผ้€‚็”จไบŽๅคงๅคšๆ•ฐ Allen-Bradley PLC๏ผŒๅŒ…ๆ‹ฌ ControlLogixใ€CompactLogix ็ญ‰ใ€‚

็‰น็‚น๏ผš

  • ไฝฟ็”จ็ฌฆๅทๅœฐๅ€๏ผˆๅฆ‚ Program:MainProgram.Counter๏ผ‰
  • ้€š่ฟ‡ Symbol Object (Class 0x6B) ่ฎฟ้—ฎๆ ‡็ญพ
  • ๆ”ฏๆŒๅฎŒๆ•ด็š„ๆ ‡็ญพ่ทฏๅพ„่งฃๆž
  • ้€‚็”จไบŽๆ ‡ๅ‡† CIP ่ฎพๅค‡

ไฝฟ็”จ็คบไพ‹๏ผš

// ๅˆ›ๅปบ่ฟžๆŽฅ
conn, err := ethernet_ip.NewTCP("192.168.1.10", nil)
if err != nil {
    log.Fatal(err)
}
defer conn.Close()

// ๅปบ็ซ‹่ฟžๆŽฅ
if err := conn.Connect(); err != nil {
    log.Fatal(err)
}

// ไฝฟ็”จๆ ‡ๅ‡† CIP ๆจกๅผ่ฏปๅ–ๆ ‡็ญพ
tags, err := conn.AllTags()
if err != nil {
    log.Fatal(err)
}

tag := tags["Program:MainProgram.IntTag"]
if err := tag.Read(); err != nil {
    log.Fatal(err)
}
fmt.Printf("ๅ€ผ: %d\n", tag.Int32())

ๆ ‡ๅ‡† CIP ๆจกๅผ่ฐƒ็”จๆต็จ‹๏ผš

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚                    ๆ ‡ๅ‡† CIP ๆจกๅผๆต็จ‹                            โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚  1. AllTags()                                                  โ”‚
โ”‚     โ””โ”€ ๆŸฅ่ฏข Symbol Object (Class 0x6B)                         โ”‚
โ”‚         โ””โ”€ ่Žทๅ–ๆ‰€ๆœ‰ๆ ‡็ญพๅ็งฐๅ’Œ็ฑปๅž‹                               โ”‚
โ”‚                                                                โ”‚
โ”‚  2. InitializeTag(name, tag)                                   โ”‚
โ”‚     โ””โ”€ ๆž„ๅปบ็ฌฆๅท่ทฏๅพ„                                             โ”‚
โ”‚         โ””โ”€ ่ฎพ็ฝฎๆ ‡็ญพ็ฑปๅž‹ๅ’Œ่ฟžๆŽฅๅผ•็”จ                               โ”‚
โ”‚                                                                โ”‚
โ”‚  3. tag.Read()                                                 โ”‚
โ”‚     โ””โ”€ SendRRData()                                            โ”‚
โ”‚         โ””โ”€ CIP Read Tag Service (0x4C)                         โ”‚
โ”‚             โ””โ”€ ่ฟ”ๅ›ž Message Router Response                    โ”‚
โ”‚                 โ””โ”€ ่งฃๆžๅ“ๅบ”ๆ•ฐๆฎๅˆฐ tag.value                     โ”‚
โ”‚                                                                โ”‚
โ”‚  4. tag.Write()                                                โ”‚
โ”‚     โ””โ”€ SendRRData()                                            โ”‚
โ”‚         โ””โ”€ CIP Write Tag Service (0x4D)                        โ”‚
โ”‚             โ””โ”€ ่ฟ”ๅ›ž็Šถๆ€็                                        โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Logix ๆจกๅผ

Logix ๆจกๅผไฝฟ็”จ Logix Class 2 ๅฏน่ฑก๏ผˆClass 0x02๏ผ‰็š„ๅฑžๆ€ง่ฎฟ้—ฎๆ–นๅผใ€‚่ฟ™็งๆจกๅผไธป่ฆ็”จไบŽไธŽ cpppo ๆœๅŠกๅ™จๆˆ–ๆŸไบ›็‰นๅฎš็š„ Logix ่ฎพๅค‡้€šไฟกใ€‚

็‰น็‚น๏ผš

  • ไฝฟ็”จๅฑžๆ€ง ID ่ฎฟ้—ฎ๏ผˆๅฆ‚ๅฑžๆ€ง 1 ๅฏนๅบ” BoolTag๏ผ‰
  • ้€š่ฟ‡ Class 2, Instance 1 ่ฎฟ้—ฎๆ ‡็ญพ
  • ไฝฟ็”จ Get Attribute Single (0x0E) ๆœๅŠก
  • ้€‚็”จไบŽ cpppo ๆจกๆ‹Ÿๅ™จๅ’Œ้ƒจๅˆ† Logix ่ฎพๅค‡

ไฝฟ็”จ็คบไพ‹๏ผš

// ๅˆ›ๅปบ่ฟžๆŽฅ
conn, err := ethernet_ip.NewTCP("127.0.0.1", nil)
if err != nil {
    log.Fatal(err)
}
defer conn.Close()

// ๅปบ็ซ‹่ฟžๆŽฅ
if err := conn.Connect(); err != nil {
    log.Fatal(err)
}

// ไฝฟ็”จ Logix ๆจกๅผ๏ผˆClass 2 ๅฑžๆ€ง่ฎฟ้—ฎ๏ผ‰่ฏปๅ–ๆ ‡็ญพ
// ๅฑžๆ€ง ID 1 = BoolTag, 3 = IntTag, 10 = RealTag
data, err := conn.ReadClass2Attribute(3) // ่ฏปๅ– IntTag
if err != nil {
    log.Fatal(err)
}

// ่งฃๆž INT ็ฑปๅž‹ๆ•ฐๆฎ
intValue := int16(binary.LittleEndian.Uint16(data[:2]))
fmt.Printf("IntTag ๅ€ผ: %d\n", intValue)

Logix ๆจกๅผ่ฐƒ็”จๆต็จ‹๏ผš

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚                    Logix ๆจกๅผๆต็จ‹                              โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚  1. ReadClass2Attribute(attrID)                                โ”‚
โ”‚     โ””โ”€ ๆž„ๅปบ CIP ่ทฏๅพ„                                           โ”‚
โ”‚         โ””โ”€ Class 2, Instance 1, Attribute attrID               โ”‚
โ”‚                                                                โ”‚
โ”‚  2. SendRRData()                                               โ”‚
โ”‚     โ””โ”€ CIP Get Attribute Single (0x0E)                         โ”‚
โ”‚         โ””โ”€ ่ฟ”ๅ›ž Message Router Response                        โ”‚
โ”‚             โ””โ”€ ๆๅ– ResponseData                               โ”‚
โ”‚                                                                โ”‚
โ”‚  3. ่งฃๆžๆ•ฐๆฎ                                                   โ”‚
โ”‚     โ””โ”€ ๆ นๆฎๅฑžๆ€ง ID ๅฏนๅบ”็š„ๆ•ฐๆฎ็ฑปๅž‹่งฃๆž                           โ”‚
โ”‚         โ””โ”€ ่ฟ”ๅ›žๅŽŸๅง‹ๅญ—่Š‚ๆ•ฐ็ป„ไพ›ไธŠๅฑ‚ๅค„็†                           โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

ๆจกๅผๅฏนๆฏ”

็‰นๆ€ง ๆ ‡ๅ‡† CIP ๆจกๅผ Logix ๆจกๅผ
่ฎฟ้—ฎๆ–นๅผ Symbolic Addressing Class 2 ๅฑžๆ€ง
ๅœฐๅ€ๆ ผๅผ Program:MainProgram.Tag ๅฑžๆ€ง ID (1-12)
้€‚็”จ่ฎพๅค‡ ๆ ‡ๅ‡† Allen-Bradley PLC cpppo ๆœๅŠกๅ™จใ€้ƒจๅˆ† Logix
ๆœๅŠก็ฑปๅž‹ Read/Write Tag (0x4C/0x4D) Get Attribute Single (0x0E)
ๆ ‡็ญพๅ‘็Žฐ AllTags() ่‡ชๅŠจๅ‘็Žฐ ้œ€้ข„ๅ…ˆ็Ÿฅ้“ๅฑžๆ€งๆ˜ ๅฐ„
UDT ๆ”ฏๆŒ โœ… โŒ

้€‰ๆ‹ฉๅปบ่ฎฎ

  • ็”Ÿไบง็Žฏๅขƒ๏ผšไฝฟ็”จ ๆ ‡ๅ‡† CIP ๆจกๅผ๏ผŒๆ”ฏๆŒๅฎŒๆ•ดๅŠŸ่ƒฝ
  • ๆต‹่ฏ•็Žฏๅขƒ๏ผšไฝฟ็”จ Logix ๆจกๅผ้…ๅˆ cpppo ๆจกๆ‹Ÿๅ™จ
  • ๅ…ผๅฎนๆ€งๆต‹่ฏ•๏ผšไธค็งๆจกๅผ้ƒฝๅบ”ๆต‹่ฏ•

cpppo ๅ…ผๅฎนๆ€ง

ๆœฌๅบ“ๅทฒๆ”ฏๆŒไธŽ cpppo ๆœๅŠกๅ™จ็š„้€šไฟกใ€‚cpppo ๆœๅŠกๅ™จไฝฟ็”จ Logix Class 2 ๅฏน่ฑกๆฅๅญ˜ๅ‚จๆ ‡็ญพ๏ผŒไธŽๆ ‡ๅ‡† CIP Symbolic Path ๆ ผๅผไธๅŒใ€‚

ๆŠ€ๆœฏ่ƒŒๆ™ฏ

cpppo ๆœๅŠกๅ™จๅฐ†ๆ ‡็ญพๆ˜ ๅฐ„ๅˆฐ Logix Class 2, Instance 1 ็š„ๅฑžๆ€ง๏ผŒไฝฟ็”จ CIP Get Attribute Single (0x0E) ๆœๅŠก่ฟ›่กŒ่ฎฟ้—ฎ๏ผš

ๆ ‡็ญพๅ ๅฑžๆ€ง ID ๆ•ฐๆฎ็ฑปๅž‹
BoolTag 1 BOOL
SintTag 2 SINT
IntTag 3 INT
DintTag 4 DINT
LintTag 5 LINT
UsintTag 6 USINT
UintTag 7 UINT
UdintTag 8 UDINT
UlintTag 9 ULINT
RealTag 10 REAL
LrealTag 11 LREAL
StringTag 12 STRING

ไฝฟ็”จๆ–นๆณ•

ไฝฟ็”จ ReadClass2Attribute ๅ’Œ WriteClass2Attribute ๆ–นๆณ•็›ดๆŽฅ่ฎฟ้—ฎ Class 2 ๅฏน่ฑก็š„ๅฑžๆ€ง๏ผš

package main

import (
    "encoding/binary"
    "fmt"
    "log"
    "math"

    "github.com/anviod/ethernet-ip"
)

func main() {
    // ๅˆ›ๅปบ่ฟžๆŽฅ
    conn, err := ethernet_ip.NewTCP("127.0.0.1", nil)
    if err != nil {
        log.Fatal(err)
    }
    defer conn.Close()

    // ๅปบ็ซ‹่ฟžๆŽฅ
    if err := conn.Connect(); err != nil {
        log.Fatal(err)
    }

    // ไฝฟ็”จ Class 2 ๅฑžๆ€ง่ฎฟ้—ฎๆ–นๅผ่ฏปๅ–ๆ ‡็ญพ
    // ๅฑžๆ€ง ID 1 = BoolTag
    data, err := conn.ReadClass2Attribute(1)
    if err != nil {
        log.Fatal(err)
    }
    fmt.Printf("BoolTag ๅ€ผ: %v\n", data[0] != 0)

    // ๅ†™ๅ…ฅ DINT ็ฑปๅž‹ๆ ‡็ญพ (ๅฑžๆ€ง ID 4 = DintTag)
    if err := conn.WriteClass2Attribute(4, []byte{0x39, 0x30, 0x00, 0x00}); err != nil {
        log.Fatal(err)
    }

    // ่ฏปๅ– REAL ็ฑปๅž‹ๆ ‡็ญพ
    data, err = conn.ReadClass2Attribute(10)
    if err != nil {
        log.Fatal(err)
    }
    floatValue := math.Float32frombits(binary.LittleEndian.Uint32(data[:4]))
    fmt.Printf("RealTag ๅ€ผ: %f\n", floatValue)
}

ๆต‹่ฏ•้ชŒ่ฏ

่ฟ่กŒ cpppo ๅ…ผๅฎนๆต‹่ฏ•๏ผš

# ๅฏๅŠจ cpppo ๆœๅŠกๅ™จ
python test/cpppo/ethernet_ip_server_cpppo.py

# ่ฟ่กŒ Go ๆต‹่ฏ•
go test -v -run TestProtocolVerifier_Cpppo

ๆต‹่ฏ•็ป“ๆžœ็คบไพ‹๏ผš

[้ชŒ่ฏ] Session ๆณจๅ†Œ
โœ“ Session ๆณจๅ†ŒๆˆๅŠŸ: 0xC17173CA

[้ชŒ่ฏ] Identity Object
โœ“ ไพ›ๅบ”ๅ•†ID: 1
โœ“ ่ฎพๅค‡็ฑปๅž‹: 14
โœ“ ไบงๅ“ไปฃ็ : 54

[้ชŒ่ฏ] ๆ•ฐๆฎ็ฑปๅž‹ๆ”ฏๆŒ (cpppo Class 2 ๆ–นๅผ)
โœ“ BoolTag (BOOL): true
โœ“ SintTag (SINT): 42
โœ“ IntTag (INT): 12345
โœ“ DintTag (DINT): 987654321
โœ“ RealTag (REAL): 3.14159
โœ“ StringTag (STRING): Hello World

========================================
cpppo ๅ…ผๅฎน้ชŒ่ฏ็ป“ๆžœๆฑ‡ๆ€ป: ้€š่ฟ‡=19, ๅคฑ่ดฅ=0
========================================

ๆณจๆ„ไบ‹้กน

  1. cpppo ๆœๅŠกๅ™จ้œ€่ฆๅ…ˆๅฏๅŠจๆ‰่ƒฝ่ฟ›่กŒๆต‹่ฏ•
  2. ้ป˜่ฎค็ซฏๅฃไธบ 44818
  3. ็กฎไฟ้˜ฒ็ซๅข™ๅ…่ฎธ TCP 44818 ็ซฏๅฃ้€šไฟก
  4. ไฝฟ็”จ Class 2 ๆ–นๅผ่ฎฟ้—ฎๆ—ถ๏ผŒๆ ‡็ญพๅ้œ€่ฆไธŽ cpppo ๆœๅŠกๅ™จ้…็ฝฎ็š„ๆ ‡็ญพๅไธ€่‡ด

API ๅ‚่€ƒ

้…็ฝฎ้€‰้กน

Config ็ป“ๆž„

type Config struct {
    TCPPort          uint16        // TCP ็ซฏๅฃ๏ผˆ้ป˜่ฎค 44818๏ผ‰
    UDPPort          uint16        // UDP ็ซฏๅฃ๏ผˆ้ป˜่ฎค 44818๏ผ‰
    Slot             uint8         // ๆŽงๅˆถๅ™จๆงฝไฝ
    TimeTick         types.USInt   // ่ฟžๆŽฅๆ—ถ้—ดๅˆปๅบฆ๏ผˆๆฏซ็ง’๏ผ‰
    TimeTickOut      types.USInt   // ่ฟžๆŽฅ่ถ…ๆ—ถ๏ผˆTimeTick ๅ•ไฝ๏ผ‰
    ConnectTimeout   time.Duration // ่ฟžๆŽฅ่ถ…ๆ—ถ๏ผˆ้ป˜่ฎค 30 ็ง’๏ผ‰
    ReadTimeout      time.Duration // ่ฏปๅ–่ถ…ๆ—ถ๏ผˆ้ป˜่ฎค 10 ็ง’๏ผ‰
    WriteTimeout     time.Duration // ๅ†™ๅ…ฅ่ถ…ๆ—ถ๏ผˆ้ป˜่ฎค 10 ็ง’๏ผ‰
    OperationTimeout time.Duration // ๆ“ไฝœ่ถ…ๆ—ถ๏ผˆ้ป˜่ฎค 15 ็ง’๏ผ‰
}

DefaultConfig() *Config

่Žทๅ–้ป˜่ฎค้…็ฝฎใ€‚

config := DefaultConfig()
config.ConnectTimeout = time.Second * 60  // ่‡ชๅฎšไน‰่ฟžๆŽฅ่ถ…ๆ—ถ
conn, err := NewTCP("192.168.1.10", config)

TCP ่ฟžๆŽฅ็ฎก็†

NewTCP(address string, config *Config) (*EIPTCP, error)

ๅˆ›ๅปบๆ–ฐ็š„ TCP ่ฟžๆŽฅๅฏน่ฑกใ€‚

conn, err := NewTCP("192.168.1.10", nil)
if err != nil {
    log.Fatal(err)
}

Connect() error

ๅปบ็ซ‹ TCP ่ฟžๆŽฅๅนถๆณจๅ†Œไผš่ฏใ€‚

err := conn.Connect()
if err != nil {
    log.Fatal(err)
}
defer conn.Close()

IsConnected() bool

ๆฃ€ๆŸฅ่ฟžๆŽฅๆ˜ฏๅฆๅทฒๅปบ็ซ‹ไธ”ไผš่ฏๅทฒๆณจๅ†Œใ€‚

if conn.IsConnected() {
    log.Println("่ฟžๆŽฅๅทฒๅปบ็ซ‹")
}

Close() error

ๅ…ณ้—ญ่ฟžๆŽฅๅนถๆณจ้”€ไผš่ฏใ€‚

err := conn.Close()

ไผš่ฏ็ฎก็†

RegisterSession() error

ๆณจๅ†Œ EIP ไผš่ฏ๏ผŒ่Žทๅ–ไผš่ฏๅฅๆŸ„ใ€‚

UnRegisterSession() error

ๆณจ้”€ๅฝ“ๅ‰ไผš่ฏใ€‚

ListIdentity() (*listIdentity.ListIdentity, error)

่Žทๅ–่ฎพๅค‡่ฏ†ๅˆซไฟกๆฏ๏ผˆ่ฎพๅค‡็ฑปๅž‹ใ€ๅŽ‚ๅ•†ใ€ๅบๅˆ—ๅท็ญ‰๏ผ‰ใ€‚

identity, err := conn.ListIdentity()
fmt.Println(identity)

ListInterface() (*listInterface.ListInterface, error)

่Žทๅ–็ฝ‘็ปœๆŽฅๅฃไฟกๆฏใ€‚

ListServices() (*listServices.ListServices, error)

่Žทๅ–่ฎพๅค‡ๆ”ฏๆŒ็š„ๆœๅŠกๅˆ—่กจใ€‚

Tag ๆ“ไฝœ

AllTags() (map[string]*Tag, error)

่Žทๅ–ๆ‰€ๆœ‰ๅฏ็”จ็š„ Tagใ€‚

tags, err := conn.AllTags()
for name, tag := range tags {
    fmt.Printf("Tag: %s, Type: %s\n", name, tag.TypeString())
}

InitializeTag(name string, tag *Tag)

ๅˆๅง‹ๅŒ–ๆŒ‡ๅฎšๅ็งฐ็š„ Tagใ€‚

var tag Tag
conn.InitializeTag("OP.UDT_Alarm.DINT_065_096", &tag)
err := tag.Read()

Tag ่ฏปๅ–

Read() error

่ฏปๅ– Tag ็š„ๅฝ“ๅ‰ๅ€ผใ€‚

err := tag.Read()
if err != nil {
    log.Fatal(err)
}
value := tag.Int32()

GetValue() interface{}

่Žทๅ– Tag ็š„ๅฝ“ๅ‰ๅ€ผ๏ผˆ่‡ชๅŠจ้€‚้…็ฑปๅž‹๏ผ‰ใ€‚

value := tag.GetValue()
switch v := value.(type) {
case int32:
    fmt.Println("DINT:", v)
case string:
    fmt.Println("STRING:", v)
}

Tag ๅ†™ๅ…ฅ

SetInt32(i int32)

่ฎพ็ฝฎ Tag ไธบ 32 ไฝๆ•ดๆ•ฐใ€‚

tag.SetInt32(123)
err := tag.Write()

SetString(s string)

่ฎพ็ฝฎ Tag ไธบๅญ—็ฌฆไธฒใ€‚

tag.SetString("hello world")
err := tag.Write()

ๅ…ถไป– Set ๆ–นๆณ•๏ผš

  • SetInt8(i int8)
  • SetUInt8(i uint8)
  • SetInt16(i int16)
  • SetUInt16(i uint16)
  • SetInt32(i int32)
  • SetUInt32(i uint32)
  • SetInt64(i int64)
  • SetUInt64(i uint64)
  • SetFloat32(f float32)
  • SetFloat64(f float64)

Tag ๅ€ผ่ฏปๅ–ๆ–นๆณ•

ๆ–นๆณ• ่ฟ”ๅ›ž็ฑปๅž‹ ่ฏดๆ˜Ž
Bool() bool ่Žทๅ–ๅธƒๅฐ”ๅ€ผ
Int8() int8 ่Žทๅ– 8 ไฝๆ•ดๆ•ฐ
UInt8() uint8 ่Žทๅ– 8 ไฝๆ— ็ฌฆๅทๆ•ดๆ•ฐ
Int16() int16 ่Žทๅ– 16 ไฝๆ•ดๆ•ฐ
UInt16() uint16 ่Žทๅ– 16 ไฝๆ— ็ฌฆๅทๆ•ดๆ•ฐ
Int32() int32 ่Žทๅ– 32 ไฝๆ•ดๆ•ฐ
UInt32() uint32 ่Žทๅ– 32 ไฝๆ— ็ฌฆๅทๆ•ดๆ•ฐ
Int64() int64 ่Žทๅ– 64 ไฝๆ•ดๆ•ฐ
UInt64() uint64 ่Žทๅ– 64 ไฝๆ— ็ฌฆๅทๆ•ดๆ•ฐ
Float32() float32 ่Žทๅ– 32 ไฝๆตฎ็‚นๆ•ฐ
Float64() float64 ่Žทๅ– 64 ไฝๆตฎ็‚นๆ•ฐ
String() string ่Žทๅ–ๅญ—็ฌฆไธฒ

ๆ‰น้‡ๆ“ไฝœ (TagGroup)

NewTagGroup(lock *sync.Mutex) *TagGroup

ๅˆ›ๅปบ Tag ็ป„ใ€‚

lock := new(sync.Mutex)
group := NewTagGroup(lock)

Add(tag *Tag)

ๆทปๅŠ  Tag ๅˆฐ็ป„ใ€‚

tag1 := tags["tag1"]
tag2 := tags["tag2"]
group.Add(tag1)
group.Add(tag2)

Read() error

ๆ‰น้‡่ฏปๅ–ๆ‰€ๆœ‰ Tagใ€‚

err := group.Read()

Write() error

ๆ‰น้‡ๅ†™ๅ…ฅๆ‰€ๆœ‰ๅทฒไฟฎๆ”น็š„ Tagใ€‚

tag1.SetInt32(100)
tag2.SetString("updated")
err := group.Write()

ๆ‰น้‡ๆ•ฐๆฎๅŒ…ๆ“ไฝœ

BatchRead(count int) ([]*Packet, error)

ไปŽ่ฟžๆŽฅๆ‰น้‡่ฏปๅ–ๅคšไธชๆ•ฐๆฎๅŒ…ใ€‚

packets, err := conn.BatchRead(10)
if err != nil {
    log.Fatal(err)
}
for _, p := range packets {
    // ๅค„็†ๆฏไธชๆ•ฐๆฎๅŒ…
}

BatchWrite(packets []*Packet) error

ๆ‰น้‡ๅ†™ๅ…ฅๅคšไธชๆ•ฐๆฎๅŒ…ๅˆฐ่ฟžๆŽฅใ€‚

packets := []*packet.Packet{packet1, packet2, packet3}
err := conn.BatchWrite(packets)
if err != nil {
    log.Fatal(err)
}

่ฏŠๆ–ญ็›‘ๆŽง

GetConnectionState() ConnectionState

่Žทๅ–ๅฝ“ๅ‰่ฟžๆŽฅ็Šถๆ€ใ€‚

state := conn.GetConnectionState()
fmt.Printf("่ฟžๆŽฅ็Šถๆ€: %s\n", state.String())
// ่พ“ๅ‡บ: Connected, Disconnected, Connecting, Reconnecting

GetConnectionStats() ConnectionStats

่Žทๅ–่ฟžๆŽฅ็ปŸ่ฎกไฟกๆฏใ€‚

stats := conn.GetConnectionStats()
fmt.Printf("่ฟžๆŽฅๆฌกๆ•ฐ: %d\n", stats.ConnectCount)
fmt.Printf("ๆ–ญๅผ€ๆฌกๆ•ฐ: %d\n", stats.DisconnectCount)
fmt.Printf("้‡่ฟžๆฌกๆ•ฐ: %d\n", stats.ReconnectCount)
fmt.Printf("ๆ€ป่ฏทๆฑ‚ๆ•ฐ: %d\n", stats.TotalRequests)
fmt.Printf("ๆˆๅŠŸ่ฏทๆฑ‚ๆ•ฐ: %d\n", stats.SuccessfulRequests)
fmt.Printf("ๅนณๅ‡ๅ“ๅบ”ๆ—ถ้—ด: %v\n", stats.AvgResponseTime)

AddConnectionListener(listener ConnectionEventListener)

ๆณจๅ†Œ่ฟžๆŽฅ็Šถๆ€ๅ˜ๅŒ–็›‘ๅฌๅ™จใ€‚

listener := func(event ConnectionEvent) {
    fmt.Printf("็Šถๆ€ๅ˜ๅŒ–: %s -> %s\n", 
        event.State.String(), 
        event.Timestamp.Format(time.RFC3339))
    if event.Error != nil {
        fmt.Printf("้”™่ฏฏ: %v\n", event.Error)
    }
}
conn.AddConnectionListener(listener)

ResetConnectionStats()

้‡็ฝฎ่ฟžๆŽฅ็ปŸ่ฎกไฟกๆฏใ€‚

conn.ResetConnectionStats()

UDP ่ฎพๅค‡ๅ‘็Žฐ

DiscoverDevices(timeout time.Duration) ([]*DiscoveredDevice, error)

ๅนฟๆ’ญๅ‘็Žฐ็ฝ‘็ปœไธญ็š„ EIP ่ฎพๅค‡ใ€‚

devices, err := ethernet_ip.DiscoverDevices(time.Second * 5)
if err != nil {
    log.Fatal(err)
}

for _, device := range devices {
    fmt.Printf("่ฎพๅค‡ๅ็งฐ: %s\n", device.DeviceName)
    fmt.Printf("IPๅœฐๅ€: %s\n", device.IPAddress)
    fmt.Printf("MACๅœฐๅ€: %s\n", device.MacAddress)
    fmt.Printf("ไบงๅ“ไปฃ็ : %d\n", device.ProductCode)
    fmt.Printf("ๅบๅˆ—ๅท: %d\n", device.SerialNumber)
}

DiscoverDevicesWithPort(timeout time.Duration, port int) ([]*DiscoveredDevice, error)

ๆŒ‡ๅฎš็ซฏๅฃ่ฟ›่กŒ่ฎพๅค‡ๅ‘็Žฐใ€‚

devices, err := ethernet_ip.DiscoverDevicesWithPort(time.Second * 5, 44818)

DiscoveredDevice ็ป“ๆž„

type DiscoveredDevice struct {
    IPAddress     net.IP   // ่ฎพๅค‡ IP ๅœฐๅ€
    MacAddress    string   // MAC ๅœฐๅ€
    DeviceName    string   // ่ฎพๅค‡ๅ็งฐ
    ProductCode   uint16   // ไบงๅ“ไปฃ็ 
    RevisionMajor uint8    // ไธป็‰ˆๆœฌๅท
    RevisionMinor uint8    // ๆฌก็‰ˆๆœฌๅท
    Status        uint16   // ่ฎพๅค‡็Šถๆ€
    SerialNumber  uint32   // ๅบๅˆ—ๅท
    VendorID      uint16   // ไพ›ๅบ”ๅ•† ID
}

TagGroup ไบ‹ๅŠกๆ€งๆ”ฏๆŒ

AtomicWrite ๅญ—ๆฎต

ๅฏ็”จๅŽŸๅญๅ†™ๅ…ฅๆจกๅผ๏ผŒ็กฎไฟๆ‰น้‡ๅ†™ๅ…ฅ็š„ๅŽŸๅญๆ€งใ€‚

group := ethernet_ip.NewTagGroup(nil)
group.AtomicWrite = true  // ๅฏ็”จไบ‹ๅŠกๆ€งๅ†™ๅ…ฅ

group.Add(tag1)
group.Add(tag2)

tag1.SetInt32(100)
tag2.SetInt32(200)

// ๅฆ‚ๆžœไปปไฝ•ไธ€ไธชๅ†™ๅ…ฅๅคฑ่ดฅ๏ผŒๆ‰€ๆœ‰ๆ ‡็ญพ็š„ changed ็Šถๆ€้ƒฝไผšๆขๅค
err := group.Write()

่ฟžๆŽฅๆฑ 

NewTCPPool(address string, config *Config, capacity int) (*EIPTCPPool, error)

ๅˆ›ๅปบ่ฟžๆŽฅๆฑ ใ€‚

pool, err := NewTCPPool("192.168.1.10", nil, 10)
if err != nil {
    log.Fatal(err)
}
defer pool.Close()

Get() (*EIPTCP, error)

ไปŽๆฑ ไธญ่Žทๅ–่ฟžๆŽฅใ€‚

conn, err := pool.Get()
if err != nil {
    log.Fatal(err)
}
defer pool.Put(conn)

Put(conn *EIPTCP) error

ๅฝ’่ฟ˜่ฟžๆŽฅๅˆฐๆฑ ไธญใ€‚

Close() error

ๅ…ณ้—ญ่ฟžๆŽฅๆฑ ใ€‚


็คบไพ‹ไปฃ็ 

ๅŸบ็ก€ไฝฟ็”จ

package main

import (
    "log"

    "github.com/anviod/ethernet-ip"
)

func main() {
    // ๅˆ›ๅปบ่ฟžๆŽฅ
    conn, err := ethernet_ip.NewTCP("192.168.1.10", nil)
    if err != nil {
        log.Fatal("ๅˆ›ๅปบ่ฟžๆŽฅๅคฑ่ดฅ:", err)
    }

    // ๅปบ็ซ‹่ฟžๆŽฅ
    err = conn.Connect()
    if err != nil {
        log.Fatal("่ฟžๆŽฅๅคฑ่ดฅ:", err)
    }
    defer conn.Close()

    // ่Žทๅ–ๆ‰€ๆœ‰ Tag
    tags, err := conn.AllTags()
    if err != nil {
        log.Fatal("่Žทๅ– Tag ๅˆ—่กจๅคฑ่ดฅ:", err)
    }

    // ่ฏปๅ–ๅ•ไธช Tag
    tag := tags["tagName"]
    err = tag.Read()
    if err != nil {
        log.Fatal("่ฏปๅ– Tag ๅคฑ่ดฅ:", err)
    }

    // ่Žทๅ–ๅ€ผ
    log.Println("Tag ๅ€ผ:", tag.Int32())
}

Tag ่ฏปๅ–ๅ’Œๅ†™ๅ…ฅ

package main

import (
    "log"

    "github.com/anviod/ethernet-ip"
)

func main() {
    conn, err := ethernet_ip.NewTCP("192.168.1.10", nil)
    if err != nil {
        log.Fatal(err)
    }
    defer conn.Close()

    if err := conn.Connect(); err != nil {
        log.Fatal(err)
    }

    // ๅˆๅง‹ๅŒ– Tag
    var tag ethernet_ip.Tag
    conn.InitializeTag("Counter", &tag)

    // ่ฏปๅ–
    if err := tag.Read(); err != nil {
        log.Fatal(err)
    }
    log.Printf("ๅฝ“ๅ‰ๅ€ผ: %d\n", tag.Int32())

    // ๅ†™ๅ…ฅ
    tag.SetInt32(12345)
    if err := tag.Write(); err != nil {
        log.Fatal(err)
    }
    log.Println("ๅ†™ๅ…ฅๆˆๅŠŸ")
}

ๆ‰น้‡ๆ“ไฝœ

package main

import (
    "log"
    "sync"

    "github.com/anviod/ethernet-ip"
)

func main() {
    conn, err := ethernet_ip.NewTCP("192.168.1.10", nil)
    if err != nil {
        log.Fatal(err)
    }
    defer conn.Close()

    if err := conn.Connect(); err != nil {
        log.Fatal(err)
    }

    tags, err := conn.AllTags()
    if err != nil {
        log.Fatal(err)
    }

    // ๅˆ›ๅปบ Tag ็ป„
    lock := new(sync.Mutex)
    group := ethernet_ip.NewTagGroup(lock)

    // ๆทปๅŠ ๅคšไธช Tag
    group.Add(tags["tag1"])
    group.Add(tags["tag2"])
    group.Add(tags["tag3"])

    // ๆ‰น้‡่ฏปๅ–
    if err := group.Read(); err != nil {
        log.Fatal(err)
    }

    // ่ฎพ็ฝฎๆ–ฐๅ€ผ
    tags["tag1"].SetInt32(100)
    tags["tag2"].SetInt32(200)
    tags["tag3"].SetString("updated")

    // ๆ‰น้‡ๅ†™ๅ…ฅ
    if err := group.Write(); err != nil {
        log.Fatal(err)
    }

    log.Println("ๆ‰น้‡ๆ“ไฝœๅฎŒๆˆ")
}

ไฝฟ็”จ่ฟžๆŽฅๆฑ 

package main

import (
    "log"

    "github.com/anviod/ethernet-ip"
)

func main() {
    // ๅˆ›ๅปบ่ฟžๆŽฅๆฑ 
    pool, err := ethernet_ip.NewTCPPool("192.168.1.10", nil, 10)
    if err != nil {
        log.Fatal(err)
    }
    defer pool.Close()

    // ่Žทๅ–่ฟžๆŽฅ
    conn, err := pool.Get()
    if err != nil {
        log.Fatal(err)
    }

    // ไฝฟ็”จ่ฟžๆŽฅ
    if err := conn.Connect(); err != nil {
        log.Fatal(err)
    }

    tags, err := conn.AllTags()
    if err != nil {
        log.Fatal(err)
    }

    log.Printf("่Žทๅ–ๅˆฐ %d ไธช Tag\n", len(tags))

    // ๅฝ’่ฟ˜่ฟžๆŽฅ
    pool.Put(conn)
}

ไฝŽ็บง API ไฝฟ็”จ

package main

import (
    "log"

    "github.com/anviod/ethernet-ip"
)

func main() {
    conn, err := ethernet_ip.NewTCP("192.168.1.10", nil)
    if err != nil {
        log.Fatal(err)
    }
    defer conn.Close()

    if err := conn.Connect(); err != nil {
        log.Fatal(err)
    }

    // ่Žทๅ–่ฎพๅค‡ไฟกๆฏ
    identities, err := conn.ListIdentity()
    if err != nil {
        log.Fatal(err)
    }
    log.Printf("่ฎพๅค‡่ฏ†ๅˆซ: %+v\n", identities)

    // ่Žทๅ–ๆŽฅๅฃไฟกๆฏ
    interfaces, err := conn.ListInterface()
    if err != nil {
        log.Fatal(err)
    }
    log.Printf("ๆŽฅๅฃไฟกๆฏ: %+v\n", interfaces)

    // ่Žทๅ–ๆœๅŠกๅˆ—่กจ
    services, err := conn.ListServices()
    if err != nil {
        log.Fatal(err)
    }
    log.Printf("ๆœๅŠกๅˆ—่กจ: %+v\n", services)
}

่ฏปๅ–ไธๅŒๆ•ฐๆฎ็ฑปๅž‹

package main

import (
    "fmt"
    "log"

    "github.com/anviod/ethernet-ip"
)

func main() {
    conn, err := ethernet_ip.NewTCP("192.168.1.10", nil)
    if err != nil {
        log.Fatal(err)
    }
    defer conn.Close()

    if err := conn.Connect(); err != nil {
        log.Fatal(err)
    }

    tags, err := conn.AllTags()
    if err != nil {
        log.Fatal(err)
    }

    for name, tag := range tags {
        if err := tag.Read(); err != nil {
            continue
        }

        fmt.Printf("Tag: %s\n", name)
        fmt.Printf("  Type: %s\n", tag.TypeString())
        fmt.Printf("  Value: %v\n", tag.GetValue())
    }
}

ๆต‹่ฏ•

ๆต‹่ฏ•ๅˆ†็ฑป

ๆœฌ้กน็›ฎๅŒ…ๅซๅคš็ง็ฑปๅž‹็š„ๆต‹่ฏ•๏ผŒ็กฎไฟไปฃ็ ่ดจ้‡ๅ’ŒๅŠŸ่ƒฝๆญฃ็กฎๆ€ง๏ผš

ๆต‹่ฏ•็ฑปๅž‹ ๆ่ฟฐ ๆ–‡ไปถไฝ็ฝฎ
ๅ•ๅ…ƒๆต‹่ฏ• ๆต‹่ฏ•ๅ•ไธชๅ‡ฝๆ•ฐๆˆ–ๆ–นๆณ•็š„่กŒไธบ *_test.go
้›†ๆˆๆต‹่ฏ• ๆต‹่ฏ•็ป„ไปถไน‹้—ด็š„ไบคไบ’ test/protocol_verifier_test.go
ๅ่ฎฎ้ชŒ่ฏๆต‹่ฏ• ้ชŒ่ฏ EIP ๅ่ฎฎๅˆ่ง„ๆ€ง test/protocol_verifier_test.go
ๆ€ง่ƒฝๆต‹่ฏ• ๆต‹่ฏ•ๆ€ง่ƒฝๆŒ‡ๆ ‡ benchmark/

ๆต‹่ฏ•ๆ–‡ไปถ็ป“ๆž„

ethernet-ip/
โ””โ”€โ”€ test/
    โ”œโ”€โ”€ protocol_verifier_test.go  # ๅ่ฎฎ้ชŒ่ฏ้›†ๆˆๆต‹่ฏ•
    โ”œโ”€โ”€ access_mode_test.go   # ่ฎฟ้—ฎๆจกๅผๆต‹่ฏ•๏ผˆCIP/Logix๏ผ‰

ๆต‹่ฏ•่ฆ†็›–่Œƒๅ›ด

1. ๆ•ฐๆฎ็ฑปๅž‹่ฏปๅ†™ๆต‹่ฏ•

ๆ”ฏๆŒๆ‰€ๆœ‰ 12 ็งๆ•ฐๆฎ็ฑปๅž‹็š„ๅ•็‹ฌ่ฏปๅ†™๏ผš

็ฑปๅž‹ ๅ†™ๅ…ฅๆต‹่ฏ• ่ฏปๅ–ๆต‹่ฏ• ้ชŒ่ฏๆ–นๅผ
BOOL SetBool() Bool() ๅธƒๅฐ”ๆฏ”่พƒ
SINT SetInt8() Int8() ๆ•ฐๅ€ผๆฏ”่พƒ
INT SetInt16() Int16() ๆ•ฐๅ€ผๆฏ”่พƒ
DINT SetInt32() Int32() ๆ•ฐๅ€ผๆฏ”่พƒ
LINT SetInt64() Int64() ๆ•ฐๅ€ผๆฏ”่พƒ
USINT SetUInt8() UInt8() ๆ•ฐๅ€ผๆฏ”่พƒ
UINT SetUInt16() UInt16() ๆ•ฐๅ€ผๆฏ”่พƒ
UDINT SetUInt32() UInt32() ๆ•ฐๅ€ผๆฏ”่พƒ
ULINT SetUInt64() UInt64() ๆ•ฐๅ€ผๆฏ”่พƒ
REAL SetFloat32() Float32() ็ฒพๅบฆๆฏ”่พƒ
LREAL SetFloat64() Float64() ็ฒพๅบฆๆฏ”่พƒ
STRING SetString() String() ๅญ—็ฌฆไธฒๆฏ”่พƒ

2. ๆ‰น้‡ๆ“ไฝœๆต‹่ฏ•

ๆต‹่ฏ• TagGroup ็š„ๆ‰น้‡่ฏปๅ†™ๅŠŸ่ƒฝ๏ผš

// ๆ‰น้‡่ฏปๅ–ๆต‹่ฏ•
group := ethernet_ip.NewTagGroup(nil)
group.Add(tag1)
group.Add(tag2)
group.Read()  // ๆ‰น้‡่ฏปๅ–

// ๆ‰น้‡ๅ†™ๅ…ฅๆต‹่ฏ•
tag1.SetInt32(100)
tag2.SetFloat32(3.14)
group.Write() // ๆ‰น้‡ๅ†™ๅ…ฅ

3. ่ฎฟ้—ฎๆจกๅผๆต‹่ฏ•

ๆต‹่ฏ•ไธค็ง่ฎฟ้—ฎๆจกๅผ๏ผš

ๆจกๅผ ๆต‹่ฏ•ๅ‡ฝๆ•ฐ ่ฎฟ้—ฎๆ–นๅผ
ๆ ‡ๅ‡† CIP ๆจกๅผ TestAccessMode_CIPMode Symbolic Addressing
Logix ๆจกๅผ TestAccessMode_LogixModeAttributeMapping Class 2 ๅฑžๆ€ง

่ฟ่กŒๆต‹่ฏ•

่ฟ่กŒๆ‰€ๆœ‰ๆต‹่ฏ•

go test ./...

่ฟ่กŒ็‰นๅฎšๆต‹่ฏ•

# ่ฟ่กŒๅ•ๅ…ƒๆต‹่ฏ•
go test -v -run "TestTag" ./...

# ่ฟ่กŒๅ่ฎฎ้ชŒ่ฏๆต‹่ฏ•
go test -v -run "TestProtocolVerifier" ./test/...

# ่ฟ่กŒๆ€ง่ƒฝๆต‹่ฏ•
go test -bench=. ./benchmark/...

่ฟ่กŒ้›†ๆˆๆต‹่ฏ•๏ผˆ้œ€่ฆ cpppo ๆœๅŠกๅ™จ๏ผ‰

# ๅฏๅŠจ cpppo ๆœๅŠกๅ™จ
python test/cpppo/ethernet_ip_server_cpppo.py

# ๅœจๅฆไธ€ไธช็ปˆ็ซฏ่ฟ่กŒๆต‹่ฏ•
go test -v -run "TestProtocolVerifier_Cpppo" ./test/...

ๆต‹่ฏ•ๆต็จ‹

1. ๅ•ๅ…ƒๆต‹่ฏ•ๆต็จ‹

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚                  ๅ•ๅ…ƒๆต‹่ฏ•ๆต็จ‹                          โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚  1. ๅˆๅง‹ๅŒ–ๆต‹่ฏ•็Žฏๅขƒ                                     โ”‚
โ”‚     โ””โ”€ ๅˆ›ๅปบ Tag ๅฏน่ฑก                                   โ”‚
โ”‚         โ””โ”€ ่ฎพ็ฝฎๆต‹่ฏ•ๆ•ฐๆฎ                                โ”‚
โ”‚                                                       โ”‚
โ”‚  2. ๆ‰ง่กŒๆต‹่ฏ•ๆ–นๆณ•                                       โ”‚
โ”‚     โ””โ”€ ่ฐƒ็”จ่ขซๆต‹ๅ‡ฝๆ•ฐ/ๆ–นๆณ•                               โ”‚
โ”‚         โ””โ”€ ๆ•่Žท่ฟ”ๅ›žๅ€ผ                                 โ”‚
โ”‚                                                       โ”‚
โ”‚  3. ้ชŒ่ฏ็ป“ๆžœ                                          โ”‚
โ”‚     โ””โ”€ ไฝฟ็”จ t.Errorf() / t.Fatalf()                   โ”‚
โ”‚         โ””โ”€ ่พ“ๅ‡บๆต‹่ฏ•ๅคฑ่ดฅไฟกๆฏ                           โ”‚
โ”‚                                                       โ”‚
โ”‚  4. ๆธ…็†่ต„ๆบ                                          โ”‚
โ”‚     โ””โ”€ ไฝฟ็”จ defer ้‡Šๆ”พ่ต„ๆบ                            โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

2. ้›†ๆˆๆต‹่ฏ•ๆต็จ‹

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚                  ้›†ๆˆๆต‹่ฏ•ๆต็จ‹                          โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚  1. ๅปบ็ซ‹่ฟžๆŽฅ                                          โ”‚
โ”‚     โ””โ”€ NewTCP() ๅˆ›ๅปบ่ฟžๆŽฅๅฏน่ฑก                          โ”‚
โ”‚         โ””โ”€ Connect() ๅปบ็ซ‹ TCP ่ฟžๆŽฅ                   โ”‚
โ”‚             โ””โ”€ RegisterSession() ๆณจๅ†Œไผš่ฏ             โ”‚
โ”‚                                                       โ”‚
โ”‚  2. ๆ‰ง่กŒๆต‹่ฏ•็”จไพ‹                                       โ”‚
โ”‚     โ”œโ”€ Session ้ชŒ่ฏ                                   โ”‚
โ”‚     โ”œโ”€ Identity ๆŸฅ่ฏข                                  โ”‚
โ”‚     โ”œโ”€ ๆ•ฐๆฎ็ฑปๅž‹่ฏปๅ†™้ชŒ่ฏ                               โ”‚
โ”‚     โ”œโ”€ Tag ๅ•็‹ฌ่ฏปๅ†™                                   โ”‚
โ”‚     โ”œโ”€ TagGroup ๆ‰น้‡ๆ“ไฝœ                              โ”‚
โ”‚     โ””โ”€ ้”™่ฏฏๅค„็†ๆต‹่ฏ•                                   โ”‚
โ”‚                                                       โ”‚
โ”‚  3. ๆ”ถ้›†็ป“ๆžœ                                          โ”‚
โ”‚     โ””โ”€ ็ปŸ่ฎก้€š่ฟ‡/ๅคฑ่ดฅๆ•ฐ้‡                              โ”‚
โ”‚         โ””โ”€ ่พ“ๅ‡บๆต‹่ฏ•ๆŠฅๅ‘Š                               โ”‚
โ”‚                                                       โ”‚
โ”‚  4. ๆธ…็†่ฟžๆŽฅ                                          โ”‚
โ”‚     โ””โ”€ Close() ๅ…ณ้—ญ่ฟžๆŽฅ                               โ”‚
โ”‚         โ””โ”€ UnRegisterSession() ๆณจ้”€ไผš่ฏ              โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

3. ๅ่ฎฎ้ชŒ่ฏๆต‹่ฏ•ๆต็จ‹

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚                ๅ่ฎฎ้ชŒ่ฏๆต‹่ฏ•ๆต็จ‹                        โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚  1. ่ฟžๆŽฅๅˆฐๆต‹่ฏ•่ฎพๅค‡                                    โ”‚
โ”‚     โ””โ”€ ่ฟžๆŽฅๅˆฐ cpppo ๆจกๆ‹Ÿๅ™จๆˆ–็œŸๅฎž PLC                  โ”‚
โ”‚                                                       โ”‚
โ”‚  2. ้ชŒ่ฏๅ่ฎฎๅˆ่ง„ๆ€ง                                     โ”‚
โ”‚     โ”œโ”€ EIP TCP ๅ่ฎฎๆกๆ‰‹                               โ”‚
โ”‚     โ”œโ”€ CIP ่ทฏๅพ„ๆž„ๅปบ                                   โ”‚
โ”‚     โ”œโ”€ Message Router ๆœๅŠก                           โ”‚
โ”‚     โ”œโ”€ Tag ่ฏปๅ†™ๆœๅŠก (0x4C/0x4D)                      โ”‚
โ”‚     โ””โ”€ Class 2 ๅฑžๆ€ง่ฎฟ้—ฎ (0x0E)                        โ”‚
โ”‚                                                       โ”‚
โ”‚  3. ้ชŒ่ฏๆ•ฐๆฎๅฎŒๆ•ดๆ€ง                                    โ”‚
โ”‚     โ”œโ”€ ๅญ—่Š‚ๅบ่ฝฌๆข                                     โ”‚
โ”‚     โ”œโ”€ ็ผ“ๅ†ฒๅŒบๆ“ไฝœ                                     โ”‚
โ”‚     โ””โ”€ ๆ•ฐๆฎ็ผ–็ /่งฃ็                                   โ”‚
โ”‚                                                       โ”‚
โ”‚  4. ็”Ÿๆˆๆต‹่ฏ•ๆŠฅๅ‘Š                                      โ”‚
โ”‚     โ””โ”€ ่พ“ๅ‡บ JSON/Markdown ๆ ผๅผๆŠฅๅ‘Š                    โ”‚
โ”‚         โ””โ”€ ไฟๅญ˜ๅˆฐ doc/ ็›ฎๅฝ•                           โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

ๆต‹่ฏ•็ป“ๆžœ็คบไพ‹

ๅ•ๅ…ƒๆต‹่ฏ•็ป“ๆžœ

=== RUN   TestTag_SetInt32
--- PASS: TestTag_SetInt32 (0.00s)
=== RUN   TestTag_GetInt16
--- PASS: TestTag_GetInt16 (0.00s)
=== RUN   TestTag_GetString
--- PASS: TestTag_GetString (0.00s)
=== RUN   TestTag_Bool
--- PASS: TestTag_Bool (0.00s)
PASS
ok      github.com/anviod/ethernet-ip    0.001s

้›†ๆˆๆต‹่ฏ•็ป“ๆžœ

[้ชŒ่ฏ] Session ๆณจๅ†Œ
โœ“ Session ๆณจๅ†ŒๆˆๅŠŸ

[้ชŒ่ฏ] Identity Object
โœ“ ไพ›ๅบ”ๅ•†ID: 1
โœ“ ่ฎพๅค‡็ฑปๅž‹: 14
โœ“ ไบงๅ“ไปฃ็ : 54
โœ“ ไบงๅ“ๅ็งฐ: cpppo
โœ“ ไบงๅ“ๅบๅˆ—ๅท: 0x12345678

[้ชŒ่ฏ] ๆ•ฐๆฎ็ฑปๅž‹ๆ”ฏๆŒ (cpppo Class 2 ๆ–นๅผ)
โœ“ BoolTag (BOOL): true
โœ“ SintTag (SINT): 42
โœ“ IntTag (INT): 12345
โœ“ DintTag (DINT): 987654321
โœ“ RealTag (REAL): 3.14159
โœ“ StringTag (STRING): Hello World

========================================
้ชŒ่ฏ็ป“ๆžœๆฑ‡ๆ€ป: ้€š่ฟ‡=19, ๅคฑ่ดฅ=0
========================================

ๆต‹่ฏ•ๆŠฅๅ‘Š

ๆต‹่ฏ•ๅฎŒๆˆๅŽไผš็”Ÿๆˆๆ€ง่ƒฝๆŠฅๅ‘Šๅˆฐ doc/ ็›ฎๅฝ•๏ผš

# ็”Ÿๆˆๆ€ง่ƒฝๆŠฅๅ‘Š
go test -bench=. -benchmem ./benchmark/... > doc/BENCHMARK_RESULT.md

ๆ€ง่ƒฝไผ˜ๅŒ–

ๆœฌๅบ“ๅทฒ่ฟ›่กŒๅคš้กนๆ€ง่ƒฝไผ˜ๅŒ–๏ผš

ๅทฒๅฎž็Žฐ็š„ไผ˜ๅŒ–

  1. ็ผ“ๅ†ฒๅŒบๆฑ ๅŒ– - ไฝฟ็”จ sync.Pool ๅค็”จ็ผ“ๅ†ฒๅŒบ๏ผŒๅ‡ๅฐ‘ GC ๅŽ‹ๅŠ›
  2. ้šๆœบๆ•ฐ็”Ÿๆˆๅ™จไผ˜ๅŒ– - ้ข„ๅˆๅง‹ๅŒ–็งๅญ๏ผŒ้ฟๅ…้‡ๅคๅˆๅง‹ๅŒ–ๅผ€้”€
  3. TCP ็ผ“ๅ†ฒๅŒบๅค็”จ - ่ฟžๆŽฅ็บงๅˆซ็š„่ฏปๅ–็ผ“ๅ†ฒๅŒบๅค็”จ
  4. ้”™่ฏฏ็Ÿญ่ทฏๆœบๅˆถ - ๆ—ฉๆœŸ้”™่ฏฏ่ฟ”ๅ›ž๏ผŒๅ‡ๅฐ‘ๆ— ๆ•ˆๆ“ไฝœ
  5. ้ข„ๅˆ†้…ๅฎน้‡ - NewWithCapacity() ๅ‡ๅฐ‘้‡ๆ–ฐๅˆ†้…

ๆ€ง่ƒฝๅŸบๅ‡†

ๆ“ไฝœ ๅžๅ้‡ ๅ†…ๅญ˜ๅˆ†้…
BufferPool_Reuse ~8000ไธ‡ ops/s 2 B/op
Tag ่ฏปๅ– ~300ไธ‡ ops/s varies
Tag ๅ†™ๅ…ฅ ~200ไธ‡ ops/s varies

่ฏฆ็ป†ๆ€ง่ƒฝๆต‹่ฏ•ๆŠฅๅ‘Š่ฏทๅ‚่€ƒ doc/PERFORMANCE_OPTIMIZATION_CN.md


็‰ˆๆœฌๆ›ดๆ–ฐ

v0.0.6 (2026-05-25)

ๆ–ฐๅขžๅŠŸ่ƒฝ

  • ๆ–ฐๅขž STRING ๅ†™ๅ…ฅไผ˜ๅŒ– - ็ฎ€ๅŒ–ๅญ—็ฌฆไธฒๅ†™ๅ…ฅ้€ป่พ‘๏ผŒๅ•ๆฌก่ฏทๆฑ‚ๅฎŒๆˆ
  • ๆ–ฐๅขž ่ถ…ๆ—ถ้…็ฝฎ - ๆ”ฏๆŒ่ฟžๆŽฅ่ถ…ๆ—ถใ€่ฏปๅ–่ถ…ๆ—ถใ€ๅ†™ๅ…ฅ่ถ…ๆ—ถใ€ๆ“ไฝœ่ถ…ๆ—ถ
  • ๆ–ฐๅขž ๆ€ง่ƒฝๅŸบๅ‡†ๆต‹่ฏ• - ๆทปๅŠ ๅฎŒๆ•ด็š„ๆ€ง่ƒฝๆต‹่ฏ•ๅฅ—ไปถ
  • ๆ–ฐๅขž ไบ‹ๅŠกๆ€งๆ”ฏๆŒ - TagGroup ๅŽŸๅญๅ†™ๅ…ฅ๏ผŒๅคฑ่ดฅ่‡ชๅŠจๅ›žๆปš
  • ๆ–ฐๅขž ่ฏŠๆ–ญ็›‘ๆŽง - ่ฟžๆŽฅ็Šถๆ€็›‘ๆŽงใ€็ปŸ่ฎกๆŒ‡ๆ ‡ๆ”ถ้›†ใ€ไบ‹ไปถ็›‘ๅฌ
  • ๆ–ฐๅขž UDP ๅนฟๆ’ญๅ‘็Žฐ - ่ฎพๅค‡่‡ชๅŠจๅ‘็ŽฐๅŠŸ่ƒฝ
  • ๆ–ฐๅขž ForwardClose - ่ฟžๆŽฅๅ…ณ้—ญๆ–นๆณ•๏ผŒ้ฟๅ…่ต„ๆบๆณ„ๆผ
  • ๆ–ฐๅขž ๆŒ‡ๆ•ฐ้€€้ฟ้‡่ฟž - ไผ˜ๅŒ–้‡่ฟžๆœบๅˆถ๏ผŒ้ฟๅ…ๆœๅŠกๅ™จๅŽ‹ๅŠ›

Bug ไฟฎๅค

  • ไฟฎๅคๅญ—็ฌฆไธฒๅ†™ๅ…ฅ้œ€่ฆไธคๆฌก่ฏทๆฑ‚็š„้—ฎ้ข˜
  • ไฟฎๅค้‡่ฟž้—ด้š”ไธบ 0 ็š„้—ฎ้ข˜

ๆ–‡ๆกฃๆ›ดๆ–ฐ

  • ๆ›ดๆ–ฐๆžถๆž„็›ฎๅฝ•็ป“ๆž„
  • ๆทปๅŠ ๆ–ฐๅŠŸ่ƒฝ API ๆ–‡ๆกฃ
  • ๆ›ดๆ–ฐๅ…ผๅฎนๆ€งๅฎกๆŸฅๆŠฅๅ‘Š

v0.0.5 (2026-05-22)

Bug ไฟฎๅค

  • ไฟฎๅค TCP ่ฟžๆŽฅๅผ‚ๅธธๆ–ญๅผ€ๅŽ็š„้‡่ฟž้€ป่พ‘
  • ไฟฎๅค ReadFromFile/WriteToFile ๆ–นๆณ•ๆœชๅฎž็Žฐ็š„ๅ ไฝ้”™่ฏฏๅค„็†
  • ไฟฎๅค BatchRead/BatchWrite ๆ–นๆณ•ๆ–‡ๆกฃ็ผบๅคฑ

ๆ–ฐๅขžๅŠŸ่ƒฝ

  • ๆ–ฐๅขž IsConnected() ๆ–นๆณ•๏ผŒ็”จไบŽๆฃ€ๆŸฅ่ฟžๆŽฅ็Šถๆ€
  • ๆ–ฐๅขž้‡่ฟžๆœบๅˆถ๏ผˆๆœ€ๅคš้‡่ฏ• 3 ๆฌก๏ผ‰
  • ๆ–ฐๅขž BatchRead() ๅ’Œ BatchWrite() ๆ‰น้‡ๆ•ฐๆฎๅŒ…ๆ“ไฝœๆ–นๆณ•
  • ๅฎŒๅ–„ๆ–‡ๆกฃๆณจ้‡Š๏ผŒ่กฅๅ…… doc.go ๅฎŒๆ•ด API ๆ–‡ๆกฃ

ๆ€ง่ƒฝไผ˜ๅŒ–

  • ไผ˜ๅŒ– TCP ็ผ“ๅ†ฒๅŒบๅค็”จ็ญ–็•ฅ
  • ไผ˜ๅŒ–่ฟžๆŽฅๆฑ ่Žทๅ–/ๅฝ’่ฟ˜้€ป่พ‘

v0.0.4 (2026-05-19)

Bug ไฟฎๅค

  • ไฟฎๅค NOP ๅ‘ฝไปค็ฉบๆ•ฐๆฎๅฏผ่‡ด cpppo ็Šถๆ€ๆœบ้”™่ฏฏ
  • ไฟฎๅค Tag.Write() ็ผ“ๅ†ฒๅŒบๅคๅˆถ้—ฎ้ข˜ (copy โ†’ append)
  • ไฟฎๅค readParser offset ่ฎก็ฎ—้”™่ฏฏ๏ผˆๆ‰ฉๅฑ•็ฑปๅž‹ 0x2a0 ๅœบๆ™ฏ๏ผ‰
  • ็งป้™ค Write() ๅ‡ฝๆ•ฐๅ†—ไฝ™็š„ nil ๆฃ€ๆŸฅ
  • ๆทปๅŠ  offset ่ดŸๅ€ผ่พน็•Œๆฃ€ๆŸฅ๏ผŒ้ฟๅ…ๅˆ‡็‰‡่ถŠ็•Œ panic

ๆ–ฐๅขžๅŠŸ่ƒฝ

  • ๆ–ฐๅขž Tag ๅ•ๅ…ƒๆต‹่ฏ•๏ผˆ14 ไธชๆต‹่ฏ•ๅ‡ฝๆ•ฐ๏ผ‰
  • ๆ–ฐๅขžๆ€ง่ƒฝๆต‹่ฏ•ๅฅ—ไปถ๏ผˆ็”Ÿๆˆ JSON/Markdown ๆŠฅๅ‘Š๏ผ‰

v0.0.3 (2026-05-18)

Bug ไฟฎๅค

  • ไฟฎๅค TagGroup ๆ‰น้‡ๅ†™ๅ…ฅๅคฑ่ดฅ้—ฎ้ข˜

ๆ–ฐๅขžๅŠŸ่ƒฝ

  • ๆทปๅŠ  cpppo ๆœๅŠกๅ™จๅ…ผๅฎนๆ€งๆ”ฏๆŒ๏ผˆLogix Class 2 ๅฏน่ฑกๆ ‡็ญพ่ฎฟ้—ฎ๏ผ‰
  • ๆ–ฐๅขž ReadClass2Attribute ๆ–นๆณ•

v0.0.2 (2026-05-17)

ๆ–ฐๅขžๅŠŸ่ƒฝ

  • ๆทปๅŠ ่ฟžๆŽฅๆฑ ๆ”ฏๆŒ๏ผˆEIPTCPPool๏ผ‰
  • ๆทปๅŠ ็ผ“ๅ†ฒๅŒบๆฑ ๅŒ–ไผ˜ๅŒ–
  • ๆ”ฏๆŒ UDT๏ผˆ็”จๆˆทๅฎšไน‰็ฑปๅž‹๏ผ‰

v0.0.1 (2026-05-16)

ๅˆๅง‹็‰ˆๆœฌ

  • ๅŸบ็ก€ TCP ่ฟžๆŽฅ็ฎก็†
  • Tag ่ฏปๅ†™ๆ“ไฝœ
  • ๆ”ฏๆŒๅŸบๆœฌๆ•ฐๆฎ็ฑปๅž‹๏ผˆBOOL, INT, DINT, REAL, STRING ็ญ‰๏ผ‰
  • ๆถˆๆฏ่ทฏ็”ฑๅ™จๅฎž็Žฐ

่ฎธๅฏ่ฏ

ๆœฌ้กน็›ฎ้‡‡็”จ WTFPL ่ฎธๅฏ่ฏใ€‚


่ดก็Œฎ

ๆฌข่ฟŽๆไบค Issue ๅ’Œ Pull Request๏ผ

About

A complete golang implementation of Ethernet/ip

Topics

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages