Code Monkey home page Code Monkey logo

gos7's People

Contributors

andreaaizza avatar applenice avatar caodj avatar cbr4yan avatar d0pam1n avatar ermanimer avatar hongjinlin avatar hongliukai avatar jekrock avatar jiekechoo avatar phagemann avatar robinson avatar srebhan avatar thinkontrolsy avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

gos7's Issues

Variables are redundant.

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
。。。
}

code quality

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

Query Bits in AGMultiRead

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):
image

The issue seems to be

gos7/multi.go

Lines 165 to 169 in ebce1a9

if dataItems[i].WordLen == s7wlbit || dataItems[i].WordLen == s7wlcounter || dataItems[i].WordLen == s7wltimer {
addr = dataItems[i].Start
} else {
addr = dataItems[i].Start * 8
}

Where bit, counter and timer addresses are parsed different. For bits this seems to be wrong, counters and timers I can't test as of right now.
Based on the screenshot above bits addresses should be parsed like the other types. As we can not specify which bit to query I suggest to add a field for bit address and ignore it for any type except bits.

panic when use PLCStop()

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

Suggestion:support PLC Type Select

Siemens have some different Device Model ,such s7200 ,s7100,s71200,s71500 ,etc. But their S7 protocol is not common。Can support enum to select?

  • Emum:
{
    S7200 = 0,
    S7300 = 1,
    S7400 = 2,
    S71200 = 3,
    S71500 = 4,
}
  • Example
func NewTCPClientHandler(model int, address string, rack int, slot int) *TCPClientHandler

There is no judgment of the error.

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

### ### }****

Runtime error while working with S7-400

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" and "ISO : Invalid Buffer passed to Send/Receive'19'"

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'

GetCPUInfo:wsarecv: An existing connection was forcibly closed by the remote host.

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

Can I connect to Profinet using this library?

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.

Date_And_Time error

gos7/helper.go

Line 170 in 929a865

func (s7 *Helper) GetDTLAt(buffer []byte, pos int) time.Time {

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)

Problems with cross compiling for darwin (serial.go)

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?

GetCPUInfo return “invalid CPU answer"

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: {    }

Suggestion: ADD new func "NewTCPClientHandlerWithConnectType" to change connectionType when allocates a new TCPClientHandler

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
}

About Set/Get PLC time function

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) {

About write bit to DB address

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

Where to start?

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

Please explain differentce between S7 and Profinet?

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?

how to detect a timed-out connection, and avoid panic on `client.AGReadDB`

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

GetAgBlockInfo return "Function not available"

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

readArea() error hiding

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

BUG : gos7.SetWStringAt()

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+i
2, uint16(c))
}
return buffer
}

AGReadMulti read fail?

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

AGreadMulti with start value > 0 results in "CPU : Address out of range"

(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?

Request support for some S7-200

Hello, I need to use the SetConnectionParameters function when using some S7-200 devices.
Can you modify the support? Thanks!

read addresses from wincc

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)

How connect s7 smart200 plc

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.

No error code if connection is dropped

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

Question: S7-200

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..

buffer is not allocated in client.Read function

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.

gos7/client.go

Lines 374 to 384 in a789e2a

func (mb *client) Read(variable string) (value interface{}, err error) {
variable = strings.ToUpper(variable) //upper
variable = strings.Replace(variable, " ", "", -1) //remove spaces
if variable == "" {
err = fmt.Errorf("input variable is empty, variable should be S7 syntax")
return
}
//var area, dbNumber, start, amount, wordLen int
var buffer []byte
switch valueArea := variable[0:2]; valueArea {

SetCharsAt

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:]...)...)
}

Best practice: Should change 'dbIndex' in 'DBX' format

@robinson , Thanks for your project 'gos7', it is very useful in my project.
I found some use tips for develpers in MultiItems write:

  • if use 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
  • and, I want to use 20 items in one AGReadMulti, PDU error, so I use 16-18, it works.
  • and I have a question: what is the mean S7DataItem.Amout, could you tell me some details?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.