danomagnum / gologix Goto Github PK
View Code? Open in Web Editor NEWEthernet/IP client library for Go inspired by pylogix that aims to be easy to use. Supports being a client and a class 3 / class 1 server.
License: MIT License
Ethernet/IP client library for Go inspired by pylogix that aims to be easy to use. Supports being a client and a class 3 / class 1 server.
License: MIT License
Currently have to split it up into multiple reads manually. This should be done automatically.
Hi,
I am trying to use your driver which is working well, and thank you for your work.
But, I am facing an issue I do not really understand. I cannot read a string using the ReadList() method, or the ReadMulti(). I always get a an error: problem reading tag {TAGNAME}: Struct!.
And if I try to read this same tag using the simple Read() method, it works out-of-the-box.
I tried with CIPTypeString and CIPTypeStruct and get the same error message.
Do you have any idea?
Thanks,
Charly
I'm trying to set up communication between a PLC and a linux server on the same subnet. I'm using the example examples/Server_Class1_V2. I edited the argument to ParsePath, but I honestly am not quite sure what these parameters mean. The PLC is in slot 0 and the ethernet module is in slot 1 (I believe, I'm not too familiar with PLC terminology). The other thing that's different from the example is that on the PLC I'm unable to create ETHERNET-BRIDGE modules, they're grayed out when I try to create a new module, so I'm attempting to use ETHERNET-MODULE. When I run the server, I am met with a connection reset by peer error message. Am I doing something wrong here? Any help would be appreciated!
server.go:
package main
import (
"log"
"os"
"time"
"github.com/danomagnum/gologix"
)
// these types will be the input and output data section for the io connection.
// the input/output nomenclature is from the PLC's point of view - Input goes to the PLC and output
// comes to us.
//
// the size (in bytes) of these structures has to match the size you set up in the IO tree for the IO connection.
// presumably you can also use other formats than bytes for the data type, but the sizes still have to match.
type InStr struct {
Data [9]byte
Count byte
}
type OutStr struct {
Data [10]byte
}
func main() {
////////////////////////////////////////////////////
// First we set up the tag providers.
//
// Each one will have a path and an object that fulfills the gologix.TagProvider interface
// We set those up and then pass them to the Router object.
// here we're using the build in io tag provider which just has 10 bytes of inputs and 10 bytes of outputs
//
////////////////////////////////////////////////////
r := gologix.PathRouter{}
// define the Input and Output instances. (Input and output here is from the plc's perspective)
inInstance := InStr{}
outInstance := OutStr{}
// an IO handler in slot 2
//p3 := gologix.IOProvider[InStr, OutStr]{}
p3 := gologix.IOProvider[InStr, OutStr]{
In: &inInstance,
Out: &outInstance,
}
path3, err := gologix.ParsePath("0,1")
if err != nil {
log.Printf("problem parsing path. %v", err)
os.Exit(1)
}
r.Handle(path3.Bytes(), &p3)
s := gologix.NewServer(&r)
go s.Serve()
t := time.NewTicker(time.Second)
for {
<-t.C
inInstance.Count++
p3.InMutex.Lock()
log.Printf("PLC Input: %v", inInstance)
p3.InMutex.Unlock()
p3.OutMutex.Lock()
log.Printf("PLC Output: %v", outInstance)
p3.OutMutex.Unlock()
}
}
PLC I/O Configuration and properties of the ETHERNET-MODULE:
Server output:
$ go run server.go
2024/05/30 18:37:36 Listening on TCP port 44818
2024/05/30 18:37:36 Listening on UDP port 2222
2024/05/30 18:37:37 PLC Input: {[0 0 0 0 0 0 0 0 0] 1}
2024/05/30 18:37:37 PLC Output: {[0 0 0 0 0 0 0 0 0 0]}
2024/05/30 18:37:38 PLC Input: {[0 0 0 0 0 0 0 0 0] 2}
2024/05/30 18:37:38 PLC Output: {[0 0 0 0 0 0 0 0 0 0]}
2024/05/30 18:37:39 PLC Input: {[0 0 0 0 0 0 0 0 0] 3}
2024/05/30 18:37:39 PLC Output: {[0 0 0 0 0 0 0 0 0 0]}
2024/05/30 18:37:40 PLC Input: {[0 0 0 0 0 0 0 0 0] 4}
2024/05/30 18:37:40 PLC Output: {[0 0 0 0 0 0 0 0 0 0]}
2024/05/30 18:37:40 new connection from 192.168.1.11:2112
2024/05/30 18:37:40 context: 0
2024/05/30 18:37:41 PLC Input: {[0 0 0 0 0 0 0 0 0] 5}
2024/05/30 18:37:41 PLC Output: {[0 0 0 0 0 0 0 0 0 0]}
2024/05/30 18:37:42 PLC Input: {[0 0 0 0 0 0 0 0 0] 6}
2024/05/30 18:37:42 PLC Output: {[0 0 0 0 0 0 0 0 0 0]}
2024/05/30 18:37:43 PLC Input: {[0 0 0 0 0 0 0 0 0] 7}
2024/05/30 18:37:43 PLC Output: {[0 0 0 0 0 0 0 0 0 0]}
2024/05/30 18:37:44 PLC Input: {[0 0 0 0 0 0 0 0 0] 8}
2024/05/30 18:37:44 PLC Output: {[0 0 0 0 0 0 0 0 0 0]}
2024/05/30 18:37:45 PLC Input: {[0 0 0 0 0 0 0 0 0] 9}
2024/05/30 18:37:45 PLC Output: {[0 0 0 0 0 0 0 0 0 0]}
2024/05/30 18:37:46 PLC Input: {[0 0 0 0 0 0 0 0 0] 10}
2024/05/30 18:37:46 PLC Output: {[0 0 0 0 0 0 0 0 0 0]}
2024/05/30 18:37:47 PLC Input: {[0 0 0 0 0 0 0 0 0] 11}
2024/05/30 18:37:47 PLC Output: {[0 0 0 0 0 0 0 0 0 0]}
2024/05/30 18:37:48 PLC Input: {[0 0 0 0 0 0 0 0 0] 12}
2024/05/30 18:37:48 PLC Output: {[0 0 0 0 0 0 0 0 0 0]}
2024/05/30 18:37:49 PLC Input: {[0 0 0 0 0 0 0 0 0] 13}
2024/05/30 18:37:49 PLC Output: {[0 0 0 0 0 0 0 0 0 0]}
2024/05/30 18:37:50 PLC Input: {[0 0 0 0 0 0 0 0 0] 14}
2024/05/30 18:37:50 PLC Output: {[0 0 0 0 0 0 0 0 0 0]}
2024/05/30 18:37:51 PLC Input: {[0 0 0 0 0 0 0 0 0] 15}
2024/05/30 18:37:51 PLC Output: {[0 0 0 0 0 0 0 0 0 0]}
2024/05/30 18:37:52 PLC Input: {[0 0 0 0 0 0 0 0 0] 16}
2024/05/30 18:37:52 PLC Output: {[0 0 0 0 0 0 0 0 0 0]}
2024/05/30 18:37:53 PLC Input: {[0 0 0 0 0 0 0 0 0] 17}
2024/05/30 18:37:53 PLC Output: {[0 0 0 0 0 0 0 0 0 0]}
2024/05/30 18:37:54 PLC Input: {[0 0 0 0 0 0 0 0 0] 18}
2024/05/30 18:37:54 PLC Output: {[0 0 0 0 0 0 0 0 0 0]}
2024/05/30 18:37:55 PLC Input: {[0 0 0 0 0 0 0 0 0] 19}
2024/05/30 18:37:55 PLC Output: {[0 0 0 0 0 0 0 0 0 0]}
2024/05/30 18:37:56 PLC Input: {[0 0 0 0 0 0 0 0 0] 20}
2024/05/30 18:37:56 PLC Output: {[0 0 0 0 0 0 0 0 0 0]}
2024/05/30 18:37:57 Error on connnection 192.168.1.11:2112. problem reading eip header. read tcp 192.168.1.205:44818->192.168.1.11:2112: read: connection reset by peer
2024/05/30 18:37:57 PLC Input: {[0 0 0 0 0 0 0 0 0] 21}
2024/05/30 18:37:57 PLC Output: {[0 0 0 0 0 0 0 0 0 0]}
2024/05/30 18:37:58 PLC Input: {[0 0 0 0 0 0 0 0 0] 22}
2024/05/30 18:37:58 PLC Output: {[0 0 0 0 0 0 0 0 0 0]}
2024/05/30 18:37:59 PLC Input: {[0 0 0 0 0 0 0 0 0] 23}
2024/05/30 18:37:59 PLC Output: {[0 0 0 0 0 0 0 0 0 0]}
2024/05/30 18:38:00 PLC Input: {[0 0 0 0 0 0 0 0 0] 24}
2024/05/30 18:38:00 PLC Output: {[0 0 0 0 0 0 0 0 0 0]}
2024/05/30 18:38:01 PLC Input: {[0 0 0 0 0 0 0 0 0] 25}
2024/05/30 18:38:01 PLC Output: {[0 0 0 0 0 0 0 0 0 0]}
2024/05/30 18:38:02 PLC Input: {[0 0 0 0 0 0 0 0 0] 26}
2024/05/30 18:38:02 PLC Output: {[0 0 0 0 0 0 0 0 0 0]}
2024/05/30 18:38:03 PLC Input: {[0 0 0 0 0 0 0 0 0] 27}
2024/05/30 18:38:03 PLC Output: {[0 0 0 0 0 0 0 0 0 0]}
2024/05/30 18:38:04 PLC Input: {[0 0 0 0 0 0 0 0 0] 28}
2024/05/30 18:38:04 PLC Output: {[0 0 0 0 0 0 0 0 0 0]}
2024/05/30 18:38:05 new connection from 192.168.1.11:2113
2024/05/30 18:38:05 context: 0
^Csignal: interrupt
client.GetAttrList for Identity Object in my testing returns expected results only for L7x controllers.
For L8z, EN2T, other in-chassis modules and PowerFlex drives I am only getting 6 bytes back
In getattr.go add a print statement and it is 0 for everything other than LOGIX5575
count, err := items[1].Int16() // Count
fmt.Println(count) // prints 0
Getting single attributes works as expected.
Is this a known issue?
Hello, when using the readmulti method, you need to pass a struct, which is identified as a tag by the fields in the struct. If I want to add 5000 fields to this struct in a loop to represent 5000 tags, what is the method?
type name struct {
TAG1 float32 `gologix:"TAG1"`
TAG2 float32 `gologix:"TAG2"`
TAG3 float32 `gologix:"TAG3"`
.....
// Dynamically add 5000 fields
TAG5000 float32 `gologix:"TAG5000"`
}
var n name
client.ReadMulti(&n)
thanks
Neither GetAttrSingle nor GenericCIPMessage takes into account the length of the class. If CIPService=0x14, CIPClass=0x04, CIPInstance=0x303 and CIPAttribute = 0x03, the PathLength is always 3, but it should be 4 actually.
Especially get attribute single and set attribute single
Multi-Read Doesn't Support Arrays
Hello, our system design currently lacks a mechanism to identify variable/tag types. As a result, I am unable to execute the Read(tag, &v)
function with v
set to the correct type before initiating communication with the Rockwell PLC.
I would like to verify if there is any existing method or recommended practice that addresses the identification of tag/variable types. Today I'm trying each type by the "most used" type we have at the PLCs and caching it to be reused.
The server doesn't handle that so it ignores it. Unfortunately that means it never replies to the client so the client potentially just sits there waiting for the response to come back.
Whenever any message comes in, a response needs to go out.
So not exactly an issue, but I since there isn't a Discussions area I figured this would be the next best option.
I was working on building a similar project, but then I ran across yours and it is much further developed than mine. I first started taking some inspiration from your project but then realized that I was narrowing in on similar methods for solving the problems as what you had taken so I figured I and hopefully others would be better served from just helping this project out.
Would you be interested/open to me issuing some fairly heavy updates to your code that don't change a huge amount but do make a few things more idiomatic to go and knock out some of the TODOs that were listed throughout the code?
If not, that is cool. I just didn't want to fork and issue a bunch of PRs that you are not interested in.
Hi,Does it support batch writing of values? Only a single write value is seen in the example directory
Line 22 in 19f2e0f
Hi,
We am using this project in our company's project. When creating multiple clients by this library to read ethernet ip devices, the ioi_cache
is possible to be accessed in the same time which causes the program is terminated. Is it possible to make it as a member of client structure to avoid concurrency issue?
// ioi.go
var ioi_cache map[string]*tagIOI
fatal error: concurrent map read and map write
goroutine 58 [running]:
github.com/danomagnum/gologix.(*Client).NewIOI(0xc000202140, {0xc000451080, 0x1b}, 0xca)
/home/goProjects/pkg/mod/github.com/danomagnum/[email protected]!beta/ioi.go:132 +0x2d5
github.com/danomagnum/gologix.(*Client).ReadList(0xc000202140, {0xc000236000, 0x50, 0x0?}, {0xc000548410, 0x50, 0xf?})
/home/goProjects/pkg/mod/github.com/danomagnum/[email protected]!beta/read.go:530 +0x1005
goroutine 8 [runnable]:
github.com/danomagnum/gologix.marshalIOIPart({0xc000451334?, 0x7?})
/home/goProjects/pkg/mod/github.com/danomagnum/[email protected]!beta/ioi.go:212 +0x22b
github.com/danomagnum/gologix.(*Client).NewIOI(0xc000212820, {0xc000741160, 0x1b}, 0xca)
/home/goProjects/pkg/mod/github.com/danomagnum/[email protected]!beta/ioi.go:198 +0x777
github.com/danomagnum/gologix.(*Client).ReadList(0xc000212820, {0xc000530a00, 0x50, 0x0?}, {0xc000436140, 0x50, 0xe?})
/home/goProjects/pkg/mod/github.com/danomagnum/[email protected]!beta/read.go:530 +0x1005
Thank you
HI,
I'm new to the Ethernet/IP protocol and thanks to this library I'm trying to develop a driver that reads from a PLC. I don't have the PLC yet, so I used the Class 3 Server example and set up some very simple tags of type Int32 and Int16.
func main() {
r := gologix.PathRouter{}
p1 := gologix.MapTagProvider{}
path1, err := gologix.ParsePath("1,0")
if err != nil {
log.Printf("problem parsing path. %v", err)
os.Exit(1)
}
r.Handle(path1.Bytes(), &p1)
p1.TagWrite("TestDint", int32(12))
p1.TagWrite("TestInt", int16(3))
s := gologix.NewServer(&r)
go s.Serve()
t := time.NewTicker(time.Second * 5)
for {
<-t.C
p1.Mutex.Lock()
log.Printf("Data 1: %v", p1.Data)
p1.Mutex.Unlock()
}
}
With another script (Client) I tried to read/write a single tag successfully following the SimpleRead and Write in the examples folder:
(I omit the client creation, connection and disconnection part)
var tag1 int32
err = client.Read("TestDint", &tag1)
if err != nil {
log.Printf("error reading TestDint. %v", err)
}
// do whatever you want with the value
log.Printf("tag1 has value %d", tag1)
so I can read well. If I want to write, I can do it successfully too because I can see its change on the server side:
var tag1 int32
err = client.Write("TestDint", tag1)
if err != nil {
log.Printf("Error writing TestDint. Error: %v", err)
}
However, if I want to use the ReadMulti() function (following the MultiRead in the examples folder) I get an error both on the client and server side:
var mr multiread
err = client.ReadMulti(&mr)
if err != nil {
log.Printf"error reading multiread. %v", err)
} else {
log.Printf"multiread struct has values %+v", mr)
}
}
Error on the Client side:
problem in read list: problem reading header from socket: EOF: < nil >
Looking in the code, I saw that this is generated by the recv_data() function in the sendreceive.go file.
On the Server side, however, I read two LOGs:
- Trying to read from MapTagProvider
- Error on connection 192.168.1.153:44188. problem with sendunitdata problem handling multi service. problem getting data from provider. tag not in map.
I understood from the LOGs that in fact the name of the tag I want to read is empty and there is a blank space.
Do you have any idea why this happens?
Thanks for your great work.
In ReadMulti(), the range that builds the lists for the ReadList() call, I believe it isn't quite right.
I believe this code
for i := range vf {
field := vf[i]
tagpath, ok := field.Tag.Lookup("gologix")
v := val.Field(i).Interface()
ct, elem := GoVarToCIPType(v)
types = append(types, ct)
elements = append(elements, elem)
if !ok {
continue
}
tags = append(tags, tagpath)
tag_map[tagpath] = i
}
should be changed to perform the !ok check before appending to types and elements. Ie, this:
for i := range vf {
field := vf[i]
tagpath, ok := field.Tag.Lookup("gologix")
if !ok {
continue
}
v := val.Field(i).Interface()
ct, elem := GoVarToCIPType(v)
types = append(types, ct)
elements = append(elements, elem)
tags = append(tags, tagpath)
tag_map[tagpath] = i
}
By doing this, ReadList() now has correct arguments wherein the indexes correctly correspond to all "gologix" tagged fields in the structure. By not doing this, ReadList() would associate a tag with an incorrect type and element.
Consider the following struct...
type pollDataMix struct {
TruthOne bool `gologix:"BOOL_ONE"`
TruthTwo bool `gologix:"BOOL_TWO"`
LocalFlag1 uint64
InspectTO int16 `gologix:"INSPECT_TIMEOUT"`
LocalFlag2 uint64
LocalFlag3 uint64
}
ReadList() would see a len(tags)=3, len(types)=6, len(elements)=6. And field InspectTO (index 2) would be assigned an incorrect type (LWORD instead of an INT). I can't see an issue with changing the code. Both of the affected lists (types, elements) are not used outside of being passed into ReadList() where they should reflect the tags list.
Create logging interface that matches slog and provide a shim for using the regular log package.
How can I set a connect or read timeout?
Because for now, it can stop after 1 minute or more.
I tried to set SocketTimeout to 1 second, but it panics immediately:
panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x80 pc=0x74d7b2]
goroutine 56 [running]:
github.com/danomagnum/gologix.(*Client).Connect(0xc0002e7b90?)
/home/charly/go/pkg/mod/github.com/danomagnum/[email protected]!beta/connect.go:14 +0x12
I am probably not doing the right thing...
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.