robinson / gos7 Goto Github PK
View Code? Open in Web Editor NEWImplementation of Siemens S7 protocol in golang
License: BSD 3-Clause "New" or "Revised" License
Implementation of Siemens S7 protocol in golang
License: BSD 3-Clause "New" or "Revised" License
func (mb *client) readArea(area int, dbNumber int, start int, amount int, wordLen int, buffer []byte) (err error) {
。。。
var response *ProtocolDataUnit
response, sendError := mb.send(&request)
err = sendError
。。。
}
Please, fix these nested if-else statements.
Early returns are much easier to read.
An example of that would be in the control.go file with the error handling.
If there is no error, the length of the data is acceptable and everything is okay, it seems that this will always return an error.
if err == nil {
if length := len(response.Data); length > 18 { // 18 is the minimum expected
if int(response.Data[19]) != pduStart {
err = fmt.Errorf(ErrorText(errCliCannotStartPLC))
} else {
if int(response.Data[20]) == pduAlreadyStarted {
err = fmt.Errorf(ErrorText(errCliAlreadyRun))
} else {
err = fmt.Errorf(ErrorText(errCliCannotStartPLC))
}
}
} else {
err = fmt.Errorf(ErrorText(errIsoInvalidPDU))
}
}
Also, there are mistakes in the Stop method.
int(response.Data[20]) == pduAlreadyStarted
Should be
int(response.Data[20]) == pduAlreadyStopped
In some use case, address port can be set other than 102
When querying a single bool in a batch (AGMultiRead), gos7 builds a different address opposed to a byte. Provided the following S7DataItemStructs
dataItems := []gos7.S7DataItem{
{s7areape, 0x01, 0, 32, 1, make([]byte, 2), ""},
{s7areape, 0x02, 0, 32, 1, make([]byte, 2), ""},
}
I would expect gos7 to query address 32. But instead it queries the following (inspected by wireshark):
The issue seems to be
Lines 165 to 169 in ebce1a9
panic: runtime error: index out of range [20] with length 20
goroutine 1 [running]:
ProtocolS7/controller/gos7.(*client).PLCStop(0xc0002cfea0?)
C:/GolangProjects/ProtocolS7/controller/gos7/control.go:73 +0x128
in this file control.go,I see this if err == nil .are you sure it's not like if err != nil ? @robinson
Siemens have some different Device Model ,such s7200 ,s7100,s71200,s71500 ,etc. But their S7 protocol is not common。Can support enum to select?
{
S7200 = 0,
S7300 = 1,
S7400 = 2,
S71200 = 3,
S71500 = 4,
}
func NewTCPClientHandler(model int, address string, rack int, slot int) *TCPClientHandler
func (mb *client) send(request *ProtocolDataUnit) (response *ProtocolDataUnit, err error) {
dataResponse, err := mb.transporter.Send(request.Data)
if err = mb.packager.Verify(request.Data, dataResponse); err != nil {
return
}
if dataResponse == nil || len(dataResponse) == 0 {
// Empty response
err = fmt.Errorf("s7: response data is empty")
return
}
response = &ProtocolDataUnit{
Data: dataResponse,
}
//check for error if any
err = responseError(response)
return response, err
I'm getting a runtime error trying to read/write to S7-400 controller. The connection is ok, but when trying read/write (no matter what) I'm getting a runtime error.
Here are log messages
tcp: 2018/08/13 15:45:09 s7: sending 03 00 00 16 11 e0 00 00 00 01 00 c0 01 0a c1 02 01 00 c2 02 01 03
tcp: 2018/08/13 15:45:09 s7: received 03 00 00 16 11 d0 00 01 44 31 00 c0 01 0a c1 02 01 00 c2 02 01 03
tcp: 2018/08/13 15:45:09 s7: sending 03 00 00 19 02 f0 80 32 01 00 00 04 00 00 08 00 00 f0 00 00 01 00 01 01 e0
tcp: 2018/08/13 15:45:09 s7: received 03 00 00 1b 02 f0 80 32 03 00 00 04 00 00 08 00 00 00 00 f0 00 00 01 00 01 01 e0
tcp: 2018/08/13 15:45:10 s7: closing connection due to idle timeout: 100.0057ms
goroutine 6 [running]:
github.com/robinson/gos7.(*tcpTransporter).Send(0xc042076080, 0xc0420121e0, 0x27, 0x50, 0x0, 0x0, 0x0, 0x0, 0x0)
C:/Users/user/go/src/github.com/robinson/gos7/tcpclient.go:110 +0x100
github.com/robinson/gos7.(*client).send(0xc0420be240, 0xc04203be38, 0x4, 0x23, 0x27)
C:/Users/user/go/src/github.com/robinson/gos7/client.go:463 +0x61
github.com/robinson/gos7.(*client).writeArea(0xc0420be240, 0x84, 0x1c7, 0x2, 0x4, 0x2, 0xc0420bc9bc, 0x4, 0x4, 0x0, ...)
C:/Users/user/go/src/github.com/robinson/gos7/client.go:350 +0x61c
github.com/robinson/gos7.(*client).AGWriteDB(0xc0420be240, 0x1c7, 0x2, 0x4, 0xc0420bc9bc, 0x4, 0x4, 0x0, 0x0)
C:/Users/user/go/src/github.com/robinson/gos7/client.go:81 +0x90
main.writeToPlc(0xc04200e100, 0xd, 0x1c7, 0x0, 0x3, 0x2, 0xc0420420c0, 0xc042040070)
C:/Users/user/go/src/client/main.go:146 +0x374
created by main.main
C:/Users/user/go/src/client/main.go:51 +0x5da
When playing the example from the README, I get the following error:
"binary.Write failed: binary.Write: invalid type int"
The only command that is working is the cpu information.
Details: I'm using S7-PLCSIM Advanced V5.0 simulator together with TIA Porta V18
Simulated CPU Model: 1511C-1 PN
Below the output of the log:
tcp: 2023/04/19 18:35:36 s7: sending 03 00 00 16 11 e0 00 00 00 01 00 c0 01 0a c1 02 01 00 c2 02 01 01
tcp: 2023/04/19 18:35:36 s7: received 03 00 00 16 11 d0 00 01 00 06 00 c0 01 0a c1 02 01 00 c2 02 01 01
tcp: 2023/04/19 18:35:36 s7: sending 03 00 00 19 02 f0 80 32 01 00 00 04 00 00 08 00 00 f0 00 00 01 00 01 01 e0
tcp: 2023/04/19 18:35:36 s7: received 03 00 00 1b 02 f0 80 32 03 00 00 04 00 00 08 00 00 00 00 f0 00 00 01 00 01 01 e0
binary.Write failed: binary.Write: invalid type int
tcp: 2023/04/19 18:35:45 s7: sending 03 00 00 25 02 f0 80 32 01 00 00 05 00 00 0e 00 06 05 01 12 0a 10 02 00 02 0a 96 84 00 00 40 00 04 00 10 00 00
tcp: 2023/04/19 18:35:46 s7: received 03 00 00 13 02 f0 80 32 02 00 00 05 00 00 00 00 00 81 04
tcp: 2023/04/19 18:35:47 s7: sending 03 00 00 1f 02 f0 80 32 01 00 00 05 00 00 0e 00 00 04 01 12 0a 10 02 00 02 0a 96 84 00 00 40
tcp: 2023/04/19 18:35:47 s7: received 03 00 00 13 02 f0 80 32 02 00 00 05 00 00 00 00 00 81 04
ISO : Invalid Buffer passed to Send/Receive'19'
main.go
const (
tcpDevice = "192.168.1.110"
rack = 0
slot = 2
)
// TCPClient
handler := gos7.NewTCPClientHandler(tcpDevice, rack, slot)
handler.Timeout = 200 * time.Second
handler.IdleTimeout = 200 * time.Second
handler.Logger = log.New(os.Stdout, "tcp: ", log.LstdFlags)
// Connect manually so that multiple requests are handled in one connection session
handler.Connect()
defer handler.Close()
//init client
client := gos7.NewClient(handler)
//cpu, err := client.GetCPUInfo()
//fmt.Println(cpu)
//fmt.Println(err)
cpi, err := client.GetCPInfo()
fmt.Println(cpi)
fmt.Println(err)
run main.go and output:
$ go run main.go
tcp: 2019/09/02 12:15:03 s7: sending 03 00 00 16 11 e0 00 00 00 01 00 c0 01 0a c1 02 01 00 c2 02 01 02
2019/09/02 12:15:04 *net.OpError read tcp 192.168.1.89:65393->192.168.1.110:102: wsarecv: An existing connection was forcibly closed by the remote host.
tcp: 2019/09/02 12:15:04 s7: sending 03 00 00 21 02 f0 80 32 07 00 00 00 01 00 08 00 08 00 01 12 04 11 44 01 00 ff 09 00 04 01 31 00 00
{0 0 0 0}
s7: response data is empty
Sry if I might be misunderstanding some basic concepts, but I need to read devices data from a Profinet Network in my application and wanted to know if this library can do that!?
I keep seeing things related to S7 protocol mixed in my searches for Profinet libraries so I'm getting a little confused.
Line 170 in 929a865
error code
Year := int(buffer[pos])*256 + int(buffer[pos+1])
Month := int(buffer[pos+2])
Day := int(buffer[pos+3])
Hour := int(buffer[pos+5])
Min := int(buffer[pos+6])
Sec := int(buffer[pos+7])
var nsec int
s7.GetValueAt(buffer, pos, &nsec)
return time.Date(Year, time.Month(Month), Day, Hour, Min, Sec, nsec, time.UTC)
reference siemen document
Date_And_Time format
tested code
year := decodeBcd(buffer[0 + pos])
if year >= 90 {
year += 1900
} else {
year += 2000
}
month := decodeBcd(buffer[1 + pos])
day := decodeBcd(buffer[2 + pos])
hour := decodeBcd(buffer[3 + pos])
minute := decodeBcd(buffer[4 + pos])
second := decodeBcd(buffer[5 + pos])
ms := decodeBcd(buffer[6 + pos])*10 + decodeBcd(buffer[7 + pos]>>4)
return time.Date(int(year), time.Month(month), day, hour, minute, second, ms*1000000, time.UTC)
Hey robinson,
first of all thank you for this package!
I am currently using your s7 implementation in one of my projects which requires me to cross compile from a linux machine to darwin. I have a lot of pain doing so because of some package importing cgo dependencies, namely "github.com/tarm/serial".
I saw you are using this package inside https://github.com/robinson/gos7/blob/master/serial.go, but the contents of this file aren't used anywhere else in this package and also don't seem to be usable for anyone using gos7.
So my question is: is serial.go crucial for this s7 implementation or could it be removed without causing any hidden errors?
test CPU:
SIMENS S7-1211
func TestGetCpuInfo(t *testing.T) {
handler := gos7.NewTCPClientHandler("192.168.1.3", 0, 1)
handler.Timeout = 5 * time.Second
handler.IdleTimeout = 5 * time.Second
defer handler.Close()
if err := handler.Connect(); err == nil {
client := gos7.NewClient(handler)
info, err := client.GetCPUInfo()
t.Log(err)
t.Log(info)
} else {
t.Fatal(err)
}
}
return
=== RUN TestGetCpuInfo
--- PASS: TestGetCpuInfo (0.07s)
plc_test.go:20: CLI : invalid CPU answer
plc_test.go:21: { }
I noticed that using NewTCPClientHandler(address string, rack int, slot int) to create a TCPClientHandler limits the connectType to 1 (AS PG_OR_PC)
func NewTCPClientHandler(address string, rack int, slot int) *TCPClientHandler {
h := &TCPClientHandler{}
h.Address = address
h.Timeout = tcpTimeout
h.IdleTimeout = tcpIdleTimeout
h.ConnectionType = connectionTypePG // Connect to the PLC as a PG
remoteTSAP := uint16(h.ConnectionType)<<8 + (uint16(rack) * 0x20) + uint16(slot)
h.setConnectionParameters(address, 0x0100, remoteTSAP)
return h
}
I often encountered when using gos7 to connect to Siemens PLCs that the PLC supports fewer PG connections and more OP connections, so I'd like to add a parameter to change this connect parameter.
// NewTCPClientHandlerWithConnectType allocates a new TCPClientHandler with connection type.
func NewTCPClientHandlerWithConnectType(address string, rack int, slot int, connectType int) *TCPClientHandler {
h := &TCPClientHandler{}
h.Address = address
h.Timeout = tcpTimeout
h.IdleTimeout = tcpIdleTimeout
h.ConnectionType = connectType
remoteTSAP := uint16(h.ConnectionType)<<8 + (uint16(rack) * 0x20) + uint16(slot)
h.setConnectionParameters(address, 0x0100, remoteTSAP)
return h
}
Hi Robinson,
PGClockWrite (implement GetPLCDateTime) , is it a mistake between 2 functions?
//implement GetPLCDateTime
func (mb *client) PGClockWrite() (datetime time.Time, err error)
//implement SetPLCDateTime
func (mb *client) PGClockRead(datetime time.Time) (err error) {
Hi Robinson,
I have a small question, as I would like to write a bit to PLC , but seems that I didn't see any function which is able to fit my requirement.
But I saw that we have the bit type for write, but the method is private.
//writeArea write generic area into PLC with following parameters: //1.area: s7areape/s7areapa/s7areamk/s7areadb/s7areact/s7areatm //2.dbnumber: specify dbnumber, to use in write DB area, otherwise = 0 //3.start: start of the address //4.amount: amount of the address //5.wordlen: bit/byte/word/dword/real/counter/timer //6.buffer: a byte array input for writing func (mb *client) writeArea(area int, dbnumber int, start int, amount int, wordlen int, buffer []byte) (err error) { var address, numElements, maxElements, totElements, dataSize, isoSize, length int
5.wordlen: bit/byte/word/dword/real/counter/timer
I would like to know why don't we export the function or create a WriteBit function?
For this case, I need to work around it to read the byte first , then change one bit , and write back , but it's not good way as it will take more time to do it .
Do you have any plan to fix this issue or have any other idea about this ?
Thanks,
Han
Line 177 in 929a865
nsec in S7 is a UDInt, which is 4 word (32bit) length,
int size may 32bit or 64bit depend on cpu architecture, may cause problem when running on 64bit cpu
Hallo
First thanks for sharing and reading my question. I have try snap7 in java and python and works. I am beginer in GO world, i just finish setup my vscode for GO and write hello.go.
Now comeback to my question, where to start? I just want to read a simple variable from let say DB100.DBW10
regards
bin mulyadin
I have an application that has to be able to read data from all Siemens PLC like 200, 300, 400, 1200.
I am not good at Siemens I am more like WAGO\Codesys\Modbus world user. I always thought that PROFINET is an analog of MODBUS in the siemens world. Now I find this library. Another protocol?
On another site, I read
The S7 protocol is designed for transferring data into SCADA systems via Ethernet. The PROFINET protocol is used for communication between PLCs and IO modules.
That sounds reasonable. Does that mean that people do not use PROFINET to communicate to Siemens PLC for SCADA? My app is a little SCADA for a particular project with S7 1200 PLCs. I need to show a few tags only. Why would I choose S7 over PROFINET?
Hi,
trying to reuse a connection client
when accessing the same Address, Rack, Slot
to avoid multiple Connect():
handler = gos7.NewTCPClientHandler(key.TcpDevice, key.Rack, key.Slot)
handler.Timeout = 20 * time.Second
handler.IdleTimeout = 20 * time.Second
if debug {
c.handler.Logger = log.New(os.Stdout, "tcp: ", log.Llongfile)
}
err = handler.Connect()
if err != nil {
log.Printf("connect err: %v", err)
return
}
client = gos7.NewClient(c.handler)
then:
err = client.AGReadDB(req.Address, req.WordStart, req.WordSize, buf)
...
if the connection drops (or goes into timeout): client.AGReadDB(req.Address, req.WordStart, req.WordSize, buf)
panics with:
panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x20 pc=0x3b5568]
goroutine 24 [running]:
github.com/robinson/gos7.(*tcpTransporter).Send(0x1c175c0, 0x1c1b300, 0x1f, 0x1f, 0x0, 0x0, 0x0, 0x0, 0x0)
github.com/robinson/[email protected]/tcpclient.go:117 +0x108
github.com/robinson/gos7.(*client).send(0x1c0d060, 0x1c62ce4, 0x1f, 0x1c1b300, 0x1dd70)
github.com/robinson/[email protected]/client.go:469 +0x44
github.com/robinson/gos7.(*client).readArea(0x1c0d060, 0x84, 0x5a, 0xc, 0x4, 0x2, 0x1c19124, 0x4, 0x4, 0x1, ...)
github.com/robinson/[email protected]/client.go:228 +0x2d0
github.com/robinson/gos7.(*client).AGReadDB(0x1c0d060, 0x5a, 0xc, 0x4, 0x1c19124, 0x4, 0x4, 0x0, 0x4)
github.com/robinson/[email protected]/client.go:76 +0x5c
...
Why is that panic-ing and how can I avoid that? E.g. detecting connection is dopped or invalid? Or reconnecting?
Cheers,
DataSeflService
Test CPU: SIEMENS S7-1211
Test Block: DB1
func TestGetBlockInfo(t *testing.T) {
handler := gos7.NewTCPClientHandler("192.168.1.3", 0, 1)
handler.Timeout = 5 * time.Second
handler.IdleTimeout = 5 * time.Second
defer handler.Close()
if err := handler.Connect(); err == nil {
client := gos7.NewClient(handler)
info, err := client.GetAgBlockInfo(65, 1)
t.Log(err)
t.Logf("BlkType: %v", info.BlkType)
t.Logf("BlkNumber: %v", info.BlkNumber)
t.Logf("BlkLang: %v", info.BlkLang)
t.Logf("BlkFlags: %v", info.BlkFlags)
t.Logf("MC7Size: %v", info.MC7Size)
t.Logf("LoadSize: %v", info.LoadSize)
t.Logf("LocalData: %v", info.LocalData)
t.Logf("SBBLength: %v", info.SBBLength)
t.Logf("CheckSum: %v", info.CheckSum)
t.Logf("Version: %v", info.Version)
t.Logf("CodeDate: %v", info.CodeDate)
t.Logf("IntfDate: %v", info.IntfDate)
t.Logf("Author: %v", info.Author)
t.Logf("Family: %v", info.Family)
t.Logf("Header: %v", info.Header)
} else {
t.Fatal(err)
}
}
return
=== RUN TestGetBlockInfo
--- PASS: TestGetBlockInfo (0.09s)
plc_test.go:35: CPU : Function not available
plc_test.go:36: BlkType: 0
plc_test.go:37: BlkNumber: 0
plc_test.go:38: BlkLang: 0
plc_test.go:39: BlkFlags: 0
plc_test.go:40: MC7Size: 0
plc_test.go:41: LoadSize: 0
plc_test.go:42: LocalData: 0
plc_test.go:43: SBBLength: 0
plc_test.go:44: CheckSum: 0
plc_test.go:45: Version: 0
plc_test.go:46: CodeDate:
plc_test.go:47: IntfDate:
plc_test.go:48: Author:
plc_test.go:49: Family:
plc_test.go:50: Header:
PASS
Line 233 in 5cd111a
this line makes buffer out of address range
I am a beginner.
I get address list like "V0.0"、“V10.1”、“I1.2”、“VD952”,how can i convert it to the address number?
Similar to previous error reported ( thanks JekRock committed on Nov 13, 2018)
in client.go line 228:
response, err := mb.send(&request)
should be
response, senderr := mb.send(&request)
err = senderr
Hi Robinson.
When I'm use gos7.SetWStringAt() to obtain the byte array written to PLC, it will always write several more bytes, For example, when I want to write the string "中文", it will finally be written as "中文$0000$0000$0000$0000". After consulting the data on the Internet, I learned that in Go, Chinese word is stored in three bytes, so using len() method to obtain the number of characters will get the wrong result, resulting in the wrong number of characters in the finally generated message, so PLC will supplement the extra "$0000" according to the wrong number of characters . To solve this problem, I modified this method, removed the parameter of the maximum number of characters, set the maximum number of characters in the method to the default 254, and calculate the number of characters of the incoming string. The following is my modification. I hope it will be helpful to you.
当我在使用gos7.SetWStringAt()这个方法来获取写入PLC的字节数组的时候,它总是会多写入几个字节,比如我要写入”中文“这个字符串的时候,它最后会写成”中文$0000$0000$0000$0000“,在网上查阅资料后,我得知在Go中,中文是用三个字节进行存储的,所以使用len()方法来获取字符数会得到错误的结果,导致最后生成的报文里的字符数是错的,所以PLC根据错误的字符数补上多余的”$0000“。为此,我修改了一下这个方法,移除了最大的字符数这个参数,在方法内将最大字符数设置为默认254,并计算传入的字符串的字符数。下面是我的修改,希望能对你有帮助。
func (s7 Helper) SetWStringAt(buffer []byte, pos int, value string) []byte {
chars := []rune(value)
sLen := len(chars)
maxlen := 254
if maxLen < sLen {
sLen = maxLen
}
s7.SetValueAt(buffer, pos+0, int16(maxLen))
s7.SetValueAt(buffer, pos+2, int16(sLen))
for i, c := range chars {
if i >= sLen {
return buffer
}
s7.SetValueAt(buffer, pos+4+i2, uint16(c))
}
return buffer
}
when i use AGReadDB
func test(){
const (
tcpDevice = "192.168.1.110"
rack = 0
slot = 1
)
// TCPClient
handler := gos7.NewTCPClientHandler(tcpDevice, rack, slot)
handler.Timeout = 200 * time.Second
handler.IdleTimeout = 200 * time.Second
handler.Logger = log.New(os.Stdout, "tcp: ", log.LstdFlags)
// Connect manually so that multiple requests are handled in one connection session
err := handler.Connect()
fmt.Println("start----------------------")
fmt.Println(err)
fmt.Println("======================")
//defer handler.Close()
//init client
client := gos7.NewClient(handler)
//client.SetSessionPassword("222")
buffer := make([]byte, 255)
//读取VD260数据
client.AGReadDB(1, 360, 4, buffer)
fmt.Println("VD360:")
//client.PLCStop()
var helper gos7.Helper
var value float32
value = helper.GetRealAt(buffer, 0)
fmt.Println(value)
}
the output log
tcp: 2019/10/22 19:54:46 s7: sending 03 00 00 19 02 f0 80 32 01 00 00 04 00 00 08 00 00 f0 00 00 01 00 01 01 e0
tcp: 2019/10/22 19:54:46 s7: received 03 00 00 1b 02 f0 80 32 03 00 00 04 00 00 08 00 00 00 00 f0 00 00 01 00 01 00 f0
start----------------------
<nil>
======================
tcp: 2019/10/22 19:54:46 s7: sending 03 00 00 1f 02 f0 80 32 01 00 00 05 00 00 0e 00 00 04 01 12 0a 10 02 00 04 00 01 84 00 0b 40
tcp: 2019/10/22 19:54:46 s7: received 03 00 00 1d 02 f0 80 32 03 00 00 05 00 00 02 00 08 00 00 04 01 ff 04 00 20 40 20 00 00
[3 0 0 29 2 240 128 50 3 0 0 5 0 0 2 0 8 0 0 4 1 255 4 0 32 64 32 0 0]
<nil>
VD360:
2.5
the VD360=2.5.
but when use AGReadMulti
func ReadMulti(client gos7.Client, items []item)(dataItems []gos7.S7DataItem, err error){
for _, val := range items {
buffer := make([]byte, 255)
switch val.type_id {
case 1:
// 示例VD
dataItems = append(dataItems, gos7.S7DataItem{
0x84,
0x02,
1,
val.number,
2,
buffer,
"",
})
}
}
err = client.AGReadMulti(dataItems, len(dataItems))
if err != nil {
beego.Debug(err)
return
}
return
}
func ReadParams(client gos7.Client, typeId int) (dataItems []gos7.S7DataItem, err error) {
var items []item
switch typeId {
case 1:
items = []item{
{320, 1},
{360, 1},
{304, 1},
}
case 2:
case 3:
default:
return nil, errors.New("read params type id error")
}
return ReadMulti(client, items)
}
the output log
tcp: 2019/10/22 19:55:13 s7: sending 03 00 00 37 02 f0 80 32 01 00 00 05 00 00 26 00 00 04 03 12 0a 10 02 00 04 00 01 84 00 01 40 12 0a 10 02 00 04 00 01 84 00 01 68 12 0a 10 02 00 04 00 01 84 00 01 30
tcp: 2019/10/22 19:55:13 s7: received 03 00 00 2d 02 f0 80 32 03 00 00 05 00 00 02 00 18 00 00 04 03 ff 04 00 20 00 00 00 00 ff 04 00 20 00 00 00 00 ff 04 00 20 00 00 00 00
[3 0 0 45 2 240 128 50 3 0 0 5 0 0 2 0 24 0 0 4 3 255 4 0 32 0 0 0 0 255 4 0 32 0 0 0 0 255 4 0 32 0 0 0 0]
<nil>
2019/10/22 19:55:13.134 [D] [SiemensController.go:32] [{132 2 1 320 4 [0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0] } {132 2 1 360 4 [0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0] } {132 2 1 304 4 [0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0] }]
the value all is 0.
@robinson @JekRock @hongjinlin @petar-dambovaliev thanks
(First: Great, ingenious tool, thank you very much!!!!)
I had some problem with AGreadMulti if start value isn't zero!
E.g. DB301[20] exists
client.AGReadDB(301, 0, 20, buffer1)
is working well
client.AGReadDB(301, 2, 18, buffer2)
is working well
client.AGReadDB(301, 10, 10, buffer3)
is working well
Also AGReadMuli is working well with
buffer4 := make([]byte, 20)
…
gos7.S7DataItem{
Area: 0x84,
WordLen: 0x02,
DBNumber: 301,
Start: 0,
Amount: 20,
Data: buffer4,
Error: error4,
},
However
buffer5 := make([]byte, 19)
…
gos7.S7DataItem{
Area: 0x84,
WordLen: 0x02,
DBNumber: 301,
Start: 1,
Amount: 19,
Data: buffer5,
Error: error5,
},
results in Error "CPU : Address out of range"
Is that a bug or a feature?
Hello, I need to use the SetConnectionParameters function when using some S7-200 devices.
Can you modify the support? Thanks!
I get addresses from wincc that communicate with PLC S7-300,
example from those addresses :
DB12522,DBB28 (of type byte)
DB14030,DBW6 (of type word)
DB5200,DD2320 (of type float)
DB6200,D206.1 (of type boolean)
can anybody please share an example of how to read it with the gos7 library? please note that I can read all those data using OPC(KEPServerEX)
hi,I can't to connect s7 smart200 plc.
This my code:
func main() {
handler := gos7.NewTCPClientHandler("192.168.2.123", 2, 0)
handler.Timeout = 10 * time.Second
err := handler.Connect()
if err != nil {
log.Printf("connect failed, device error: %s\n", err)
panic(err)
}
}
error info:
2023/06/15 19:38:16 *net.OpError read tcp 192.168.1.36:62273->192.168.2.123:102: wsarecv: An existing connection was forcibly closed by the remote host.
2023/06/15 19:38:16 connect failed, device error: ISO : Invalid PDU received
panic: ISO : Invalid PDU received
Using other client connections can succeed. May I ask if it was written incorrectly?
Looking forward to your reply, thanks.
I had the following bug. I sent two AGWriteDB requests and between these two requests the connection to plc was dropped (I've emulated this by disconnecting the network on PC). After the second request I didn't get any error but there was no data in DB. And after all following request the same.
I've found that the bug was in this line. Here the error message from this line should be returned, but it's kinda hidden by err variable from the writeArea function (I'm not an expert in Go, so I don't know how it has happened. I'll be gratefull if you explain this).
I've changed error variable name and it has solved the bug.
Please check the pull request #6
please can you advise if there is a function to read booleans?
@robinson
Hi , I want to know when will support ppi communication?
Line 120 in 929a865
not 1900, should be 1990
tested on S7-1500
Hey man,
i really want to use your awesome work... Im running an Siemens S7-200 with an CP243-1 as ethernet module and my end goal is to set a marker and therefor control an output. simple stuff...
Now to my problem. The connection handler asks for an rack and slot number which i don't seem to find. Do you have any tips for finding this number? Is it in my Project or CPU defined?
Any help is appreciated. Thx..
Hello,
Thank you for the S7 library. I'd like to report a bug. In the client.Read
function, buffer is not allocated in line 383 as below, which causes panic while copying response.Data in client.readArea
. I am not sure if it is wise to make var buffer = make([]byte, 255)
in client.Read
function or changing function signature to pass buffer as a parameter.
Lines 374 to 384 in a789e2a
the SetCharsAt function does not work. I tried converting from string to a byte slice, writing to plc, and reading back using GetCharsAt. The values return all zeros.
ive found the solution below to work
//SetCharsAt Set Array of char (S7 ARRAY OF CHARS)
func (s7 *Helper) SetCharsAt(buffer []byte, pos int, value string) {
copy(buffer[pos:pos+len(value)], []byte(value))
//buffer = append(buffer[:pos], append([]byte(value), buffer[pos:]...)...)
}
Great project @robinson !
Can you please post an example of Multiple Read?
Thanks
Such as the title.Thank you!
add wstring support
@robinson , Thanks for your project 'gos7', it is very useful in my project.
I found some use tips for develpers in MultiItems write:
DBX
format, change dbIndex
, for example: DB1.DBX12.5
, the startaddress
should be dbIndex + dbBit (DBX12.5 = 12<<3 + 5 = 96+5 = 101 = 0x65)
, in my case:dbBit, _ := strconv.ParseInt(string(string(dbArray[2])), 10, 16)
dbIndex = dbIndex<<3 + dbBit
20
items in one AGReadMulti
, PDU error, so I use 16-18
, it works.S7DataItem.Amout
, could you tell me some details?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.