diff --git a/cgo_util.go b/cgo_util.go new file mode 100644 index 0000000..1019533 --- /dev/null +++ b/cgo_util.go @@ -0,0 +1,28 @@ +package iec61850 + +import "C" +import sc "golang.org/x/text/encoding/simplifiedchinese" + +func C2GoStr(str *C.char) string { + utf8str, _ := sc.GB18030.NewDecoder().String(C.GoString(str)) + return utf8str +} + +func Go2CStr(str string) *C.char { + gbstr, _ := sc.GB18030.NewEncoder().String(str) + return C.CString(gbstr) +} + +func C2GoBool(i C.int) bool { + if i == 1 { + return true + } + return false +} + +func Go2CBool(b bool) C.int { + if b { + return 1 + } + return 0 +} diff --git a/client.go b/client.go index 7f86ff5..bd58040 100644 --- a/client.go +++ b/client.go @@ -3,6 +3,7 @@ package iec61850 // #include import "C" import ( + "fmt" "sync/atomic" "unsafe" ) @@ -162,6 +163,135 @@ func (c *Client) Read(objectRef string, fc FC) (interface{}, error) { return c.toGoValue(mmsValue, mmsType), nil } +func (c *Client) GetLogicalDeviceList() DataModel { + var clientError C.IedClientError + deviceList := C.IedConnection_getLogicalDeviceList(c.conn, &clientError) + + var dataModel DataModel + + device := deviceList.next + for device != nil { + + var ld LD + ld.Data = C2GoStr((*C.char)(device.data)) + + logicalNodes := C.IedConnection_getLogicalDeviceDirectory(c.conn, &clientError, (*C.char)(device.data)) + logicalNode := logicalNodes.next + + for logicalNode != nil { + var ln LN + ln.Data = C2GoStr((*C.char)(logicalNode.data)) + + lnRef := fmt.Sprintf("%s/%s", ld.Data, C2GoStr((*C.char)(logicalNode.data))) + + cRef := Go2CStr(lnRef) + dataObjects := C.IedConnection_getLogicalNodeDirectory(c.conn, &clientError, cRef, C.ACSI_CLASS_DATA_OBJECT) + dataObject := dataObjects.next + for dataObject != nil { + var do DO + do.Data = C2GoStr((*C.char)(dataObject.data)) + + dataObject = dataObject.next + doRef := fmt.Sprintf("%s/%s.%s", C2GoStr((*C.char)(device.data)), C2GoStr((*C.char)(logicalNode.data)), do.Data) + + var das []DA + c.GetDAs(doRef, das) + + do.DAs = das + ln.DOs = append(ln.DOs, do) + } + + C.LinkedList_destroy(dataObjects) + + dataSets := C.IedConnection_getLogicalNodeDirectory(c.conn, &clientError, Go2CStr(lnRef), C.ACSI_CLASS_DATA_SET) + dataSet := dataSets.next + for dataSet != nil { + var ds DS + ds.Data = C2GoStr((*C.char)(dataSet.data)) + + var isDeletable C.bool + dataSetRef := fmt.Sprintf("%s.%s", lnRef, ds.Data) + dataSetMembers := C.IedConnection_getDataSetDirectory(c.conn, &clientError, Go2CStr(dataSetRef), &isDeletable) + + if isDeletable { + fmt.Println(fmt.Sprintf(" Data set: %s (deletable)", ds.Data)) + } else { + fmt.Println(fmt.Sprintf(" Data set: %s (not deletable)", ds.Data)) + } + + dataSetMemberRef := dataSetMembers.next + for dataSetMemberRef != nil { + var dsRef DSRef + dsRef.Data = C2GoStr((*C.char)(dataSetMemberRef.data)) + ds.DSRefs = append(ds.DSRefs, dsRef) + + dataSetMemberRef = dataSetMemberRef.next + } + C.LinkedList_destroy(dataSetMembers) + dataSet = dataSet.next + ln.DSs = append(ln.DSs, ds) + } + + C.LinkedList_destroy(dataSets) + + reports := C.IedConnection_getLogicalNodeDirectory(c.conn, &clientError, Go2CStr(lnRef), C.ACSI_CLASS_URCB) + report := reports.next + for report != nil { + var r URReport + r.Data = C2GoStr((*C.char)(report.data)) + ln.URReports = append(ln.URReports, r) + + report = report.next + } + C.LinkedList_destroy(reports) + + reports = C.IedConnection_getLogicalNodeDirectory(c.conn, &clientError, Go2CStr(lnRef), C.ACSI_CLASS_BRCB) + report = reports.next + for report != nil { + var r BRReport + r.Data = C2GoStr((*C.char)(report.data)) + ln.BRReports = append(ln.BRReports, r) + + report = report.next + } + + C.LinkedList_destroy(reports) + + ld.LNs = append(ld.LNs, ln) + + logicalNode = logicalNode.next + } + C.LinkedList_destroy(logicalNodes) + + dataModel.LDs = append(dataModel.LDs, ld) + + device = device.next + } + C.LinkedList_destroy(deviceList) + return dataModel +} + +func (c *Client) GetDAs(doRef string, das []DA) { + + var clientError C.IedClientError + dataAttributes := C.IedConnection_getDataDirectory(c.conn, &clientError, Go2CStr(doRef)) + defer C.LinkedList_destroy(dataAttributes) + if dataAttributes != nil { + dataAttribute := dataAttributes.next + + for dataAttribute != nil { + var da DA + da.Data = C2GoStr((*C.char)(dataAttribute.data)) + das = append(das, da) + + dataAttribute = dataAttribute.next + daRef := fmt.Sprintf("%s.%s", doRef, da.Data) + c.GetDAs(daRef, das) + } + } + +} + // ReadDataSet 读取DataSet func (c *Client) ReadDataSet(objectRef string) ([]*MmsValue, error) { cObjectRef := C.CString(objectRef) diff --git a/data_model.go b/data_model.go new file mode 100644 index 0000000..f7a6e80 --- /dev/null +++ b/data_model.go @@ -0,0 +1,45 @@ +package iec61850 + +type DataModel struct { + LDs []LD +} + +type LD struct { + Data string + LNs []LN +} + +type LN struct { + Data string + DOs []DO + DSs []DS + URReports []URReport + BRReports []BRReport +} + +type URReport struct { + Data string +} + +type BRReport struct { + Data string +} + +type DS struct { + Data string + DSRefs []DSRef +} + +type DSRef struct { + Data string +} + +type DO struct { + Data string + DAs []DA +} + +type DA struct { + Data string + DAs []DA +} diff --git a/go.mod b/go.mod index 8c08877..935dc4e 100644 --- a/go.mod +++ b/go.mod @@ -2,4 +2,7 @@ module github.com/wendy512/iec61850 go 1.19 -require github.com/spf13/cast v1.6.0 // indirect +require ( + github.com/spf13/cast v1.6.0 + golang.org/x/text v0.14.0 +) diff --git a/go.sum b/go.sum index 07162cf..995f5a7 100644 --- a/go.sum +++ b/go.sum @@ -1,2 +1,9 @@ +github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0= github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= +golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= diff --git a/scl_xml/scl.go b/scl_xml/scl.go index 1c1608c..21cdbf7 100644 --- a/scl_xml/scl.go +++ b/scl_xml/scl.go @@ -130,6 +130,7 @@ type LN0 struct { LnType string `xml:"lnType,attr"` LnClass string `xml:"lnClass,attr"` DataSets []DataSet `xml:"DataSet"` + DOI []DOI `xml:"DOI"` } type LN struct { diff --git a/test/client_test.go b/test/client_test.go index 07cfdbe..cc55ff3 100644 --- a/test/client_test.go +++ b/test/client_test.go @@ -1,6 +1,8 @@ package test import ( + "encoding/json" + "fmt" "github.com/wendy512/iec61850" "testing" ) @@ -57,3 +59,21 @@ func doRead(t *testing.T, client *iec61850.Client, objectRef string, fc iec61850 } t.Logf("read %s value -> %v", objectRef, value) } + +func TestGetLogicalDeviceList(t *testing.T) { + client, err := iec61850.NewClient(&iec61850.Settings{ + Host: "127.0.0.1", + Port: 10086, + ConnectTimeout: 10000, + RequestTimeout: 10000, + }) + if err != nil { + panic(err) + } + deviceList := client.GetLogicalDeviceList() + marshal, err := json.Marshal(deviceList) + if err != nil { + panic(err) + } + fmt.Println(string(marshal)) +}