diff --git a/client_ld.go b/client_ld.go index 5aa607a..3a27bc5 100644 --- a/client_ld.go +++ b/client_ld.go @@ -23,101 +23,95 @@ func (c *Client) GetLogicalDeviceList() DataModel { 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) - defer C.free(unsafe.Pointer(cRef)) - 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) - - clnRef := Go2CStr(lnRef) - defer C.free(unsafe.Pointer(clnRef)) - - dataSets := C.IedConnection_getLogicalNodeDirectory(c.conn, &clientError, clnRef, 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) - - cdataSetRef := Go2CStr(dataSetRef) - defer C.free(unsafe.Pointer(cdataSetRef)) - - dataSetMembers := C.IedConnection_getDataSetDirectory(c.conn, &clientError, cdataSetRef, &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)) + func() { + 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) + defer C.free(unsafe.Pointer(cRef)) + 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) } - - 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(dataObjects) + clnRef := Go2CStr(lnRef) + defer C.free(unsafe.Pointer(clnRef)) + dataSets := C.IedConnection_getLogicalNodeDirectory(c.conn, &clientError, clnRef, C.ACSI_CLASS_DATA_SET) + dataSet := dataSets.next + for dataSet != nil { + func() { + var ds DS + ds.Data = C2GoStr((*C.char)(dataSet.data)) + var isDeletable C.bool + dataSetRef := fmt.Sprintf("%s.%s", lnRef, ds.Data) + cdataSetRef := Go2CStr(dataSetRef) + defer C.free(unsafe.Pointer(cdataSetRef)) + + dataSetMembers := C.IedConnection_getDataSetDirectory(c.conn, &clientError, cdataSetRef, &isDeletable) + if isDeletable { + fmt.Printf(" Data set: %s (deletable)\n", ds.Data) + } else { + fmt.Printf(" Data set: %s (not deletable)\n", 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(dataSetMembers) - dataSet = dataSet.next - ln.DSs = append(ln.DSs, ds) - } - - C.LinkedList_destroy(dataSets) + C.LinkedList_destroy(dataSets) - clnRef1 := Go2CStr(lnRef) - defer C.free(unsafe.Pointer(clnRef1)) + clnRef1 := Go2CStr(lnRef) + defer C.free(unsafe.Pointer(clnRef1)) - reports := C.IedConnection_getLogicalNodeDirectory(c.conn, &clientError, clnRef1, 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) + reports := C.IedConnection_getLogicalNodeDirectory(c.conn, &clientError, clnRef1, 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) + report = report.next + } + C.LinkedList_destroy(reports) - clnRef2 := Go2CStr(lnRef) - defer C.free(unsafe.Pointer(clnRef2)) + clnRef2 := Go2CStr(lnRef) + defer C.free(unsafe.Pointer(clnRef2)) - reports = C.IedConnection_getLogicalNodeDirectory(c.conn, &clientError, clnRef2, 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) + reports = C.IedConnection_getLogicalNodeDirectory(c.conn, &clientError, clnRef2, 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 - } + report = report.next + } - C.LinkedList_destroy(reports) + C.LinkedList_destroy(reports) - ld.LNs = append(ld.LNs, ln) + ld.LNs = append(ld.LNs, ln) - logicalNode = logicalNode.next + logicalNode = logicalNode.next + }() } C.LinkedList_destroy(logicalNodes) diff --git a/client_report.go b/client_report.go index f059c13..9c8c044 100644 --- a/client_report.go +++ b/client_report.go @@ -15,13 +15,13 @@ var reportCallbacks = make(map[int32]*reportCallbackHandler) type ReasonForInclusion int const ( - IEC61850_REASON_NOT_INCLUDED ReasonForInclusion = iota - IEC61850_REASON_DATA_CHANGE - IEC61850_REASON_QUALITY_CHANGE - IEC61850_REASON_DATA_UPDATE - IEC61850_REASON_INTEGRITY - IEC61850_REASON_GI - IEC61850_REASON_UNKNOWN + IEC61850_REASON_NOT_INCLUDED ReasonForInclusion = 0 + IEC61850_REASON_DATA_CHANGE ReasonForInclusion = 1 + IEC61850_REASON_QUALITY_CHANGE ReasonForInclusion = 2 + IEC61850_REASON_DATA_UPDATE ReasonForInclusion = 4 + IEC61850_REASON_INTEGRITY ReasonForInclusion = 8 + IEC61850_REASON_GI ReasonForInclusion = 16 + IEC61850_REASON_UNKNOWN ReasonForInclusion = 32 ) type reportCallbackHandler struct { diff --git a/config_win64.go b/config_win64.go index 13c0df0..f695600 100644 --- a/config_win64.go +++ b/config_win64.go @@ -3,5 +3,5 @@ package iec61850 // #cgo CFLAGS: -I./libiec61850/inc/hal/inc -I./libiec61850/inc/common/inc -I./libiec61850/inc/goose -I./libiec61850/inc/iec61850/inc -I./libiec61850/inc/iec61850/inc_private -I./libiec61850/inc/logging -I./libiec61850/inc/mms/inc -I./libiec61850/inc/mms/inc_private -I./libiec61850/inc/mms/iso_mms/asn1c -// #cgo LDFLAGS: -static-libgcc -static-libstdc++ -L./libiec61850/lib/win64 -liec61850 -lws2_32 +// #cgo LDFLAGS: -static-libgcc -static-libstdc++ -L${SRCDIR}/libiec61850/lib/win64 -liec61850 -lhal -lws2_32 import "C" diff --git a/goose_publisher.go b/goose_publisher.go new file mode 100644 index 0000000..3c0f229 --- /dev/null +++ b/goose_publisher.go @@ -0,0 +1,148 @@ +package iec61850 + +/* +#include "goose_publisher.h" +#include "mms_value.h" + +static bool is_publisher_not_null(GoosePublisher p) { + return p != NULL; +} + +static void destroy_linked_list_val(LinkedList value) { + LinkedList_destroyDeep(value, (LinkedListValueDeleteFunction)MmsValue_delete); +} +*/ +import "C" +import ( + "errors" + "unsafe" +) + +type ( + LinkedListValue struct { + internalLinkedList *C.struct_sLinkedList + } + + GoosePublisherConf struct { + InterfaceID string + AppID uint16 + DstAddr [6]uint8 + VlanID uint16 + VlanPriority uint8 + } + + GoosePublisher struct { + internalPublisher *C.struct_sGoosePublisher + } +) + +var ( + ErrCreateGoosePublisher = errors.New("can not create goose publisher") + ErrSendGooseValue = errors.New("can not send goose value") +) + +func NewGoosePublisher(conf GoosePublisherConf) (publisher *GoosePublisher, err error) { + parameters := C.struct_sCommParameters{} + parameters.appId = C.uint16_t(conf.AppID) + parameters.vlanId = C.uint16_t(conf.VlanID) + parameters.vlanPriority = C.uint8_t(conf.VlanPriority) + for i := 0; i < len(conf.DstAddr); i++ { + parameters.dstAddress[i] = C.uint8_t(conf.DstAddr[i]) + } + ether := C.CString(conf.InterfaceID) + defer C.free(unsafe.Pointer(ether)) + + cGoosePublisher := C.GoosePublisher_create(¶meters, ether) + if !bool(C.is_publisher_not_null(cGoosePublisher)) { + err = ErrCreateGoosePublisher + return + } + + publisher = &GoosePublisher{ + internalPublisher: cGoosePublisher, + } + + return +} + +func (receiver *GoosePublisher) SetGoCbRef(goCbRef string) { + ref := C.CString(goCbRef) + defer C.free(unsafe.Pointer(ref)) + + C.GoosePublisher_setGoCbRef(receiver.internalPublisher, ref) +} + +func (receiver *GoosePublisher) SetDataSetRef(dataSetRef string) { + ref := C.CString(dataSetRef) + defer C.free(unsafe.Pointer(ref)) + + C.GoosePublisher_setDataSetRef(receiver.internalPublisher, ref) +} + +func (receiver *GoosePublisher) SetConfRev(confRef uint32) { + C.GoosePublisher_setConfRev(receiver.internalPublisher, C.uint32_t(confRef)) +} + +func (receiver *GoosePublisher) SetTimeAllowedToLive(timeAllowedToLive uint32) { + C.GoosePublisher_setTimeAllowedToLive(receiver.internalPublisher, C.uint32_t(timeAllowedToLive)) +} + +func (receiver *GoosePublisher) SetSimulation(simulation bool) { + C.GoosePublisher_setSimulation(receiver.internalPublisher, C.bool(simulation)) +} + +func (receiver *GoosePublisher) SetStNum(stNum uint32) { + C.GoosePublisher_setStNum(receiver.internalPublisher, C.uint32_t(stNum)) +} + +func (receiver *GoosePublisher) SetSqNum(sqNum uint32) { + C.GoosePublisher_setSqNum(receiver.internalPublisher, C.uint32_t(sqNum)) +} + +func (receiver *GoosePublisher) SetNeedsCommission(ndsCom bool) { + C.GoosePublisher_setNeedsCommission(receiver.internalPublisher, C.bool(ndsCom)) +} + +func (receiver *GoosePublisher) IncreaseStNum() { + C.GoosePublisher_increaseStNum(receiver.internalPublisher) +} + +func (receiver *GoosePublisher) Reset() { + C.GoosePublisher_reset(receiver.internalPublisher) +} + +func (receiver *GoosePublisher) Publish(dataSet *LinkedListValue) error { + if int(C.GoosePublisher_publish(receiver.internalPublisher, dataSet.internalLinkedList)) == -1 { + return ErrSendGooseValue + } + + return nil +} + +func (receiver *GoosePublisher) Close() { + C.GoosePublisher_destroy(receiver.internalPublisher) +} + +func NewLinkedListValue() *LinkedListValue { + return &LinkedListValue{ + internalLinkedList: C.LinkedList_create(), + } +} + +func (receiver *LinkedListValue) Add(value *MmsValue) error { + rawVal, err := toMmsValue(value.Type, value.Value) + if err != nil { + return err + } + C.LinkedList_add(receiver.internalLinkedList, unsafe.Pointer(rawVal)) + + return nil +} + +func (receiver *LinkedListValue) Size() int { + return int(C.LinkedList_size(receiver.internalLinkedList)) +} + +func (receiver *LinkedListValue) Destroy() { + C.destroy_linked_list_val(receiver.internalLinkedList) +} diff --git a/goose_receiver.go b/goose_receiver.go new file mode 100644 index 0000000..48e4a61 --- /dev/null +++ b/goose_receiver.go @@ -0,0 +1,140 @@ +package iec61850 + +/* +#include "goose_receiver.h" +#include "goose_subscriber.h" +#include + +extern void cgoReportCallbackBridgeDispatcher(GooseSubscriber subscriber, void *parameter); + +static void goose_report_proxy_handler(GooseSubscriber subscriber, void* parameter) { + cgoReportCallbackBridgeDispatcher(subscriber, parameter); +} + +static void simple_goose_subscriber_set_listener(GooseSubscriber subscriber, uintptr_t parameter) { + GooseSubscriber_setListener(subscriber, goose_report_proxy_handler, (void *)parameter); +} +*/ +import "C" +import ( + "sync" + "sync/atomic" + "unsafe" +) + +type ( + GooseReceiver struct { + noCopy struct{} + gooseReceiver *C.struct_sGooseReceiver + } +) + +var ( + gooseCallbackLocker struct { + noCopy struct{} + sync.RWMutex + idOffset atomic.Uintptr + callbackRefs map[GooseCallbackHandlerID]struct { + handler GooseReportCallback + subscriber *GooseSubscriber + } + } +) + +func init() { + gooseCallbackLocker.Lock() + defer gooseCallbackLocker.Unlock() + + gooseCallbackLocker.idOffset.Add(1000) + gooseCallbackLocker.callbackRefs = map[GooseCallbackHandlerID]struct { + handler GooseReportCallback + subscriber *GooseSubscriber + }{} +} + +//export cgoReportCallbackBridgeDispatcher +func cgoReportCallbackBridgeDispatcher(_ *C.struct_sGooseSubscriber, parameter unsafe.Pointer) { + refID := GooseCallbackHandlerID(parameter) + gooseCallbackLocker.RLock() + defer gooseCallbackLocker.RUnlock() + + if fetch, ok := gooseCallbackLocker.callbackRefs[refID]; ok { + fetch.handler(&GooseReport{ + parameter: parameter, + GooseSubscriber: fetch.subscriber, + }) + } +} + +func NewGooseReceiver() *GooseReceiver { + return &GooseReceiver{ + gooseReceiver: C.GooseReceiver_create(), + } +} + +func (receiver *GooseReceiver) AddSubscriber(subscriber *GooseSubscriber) *GooseReceiver { + gooseCallbackLocker.Lock() + defer gooseCallbackLocker.Unlock() + + gooseCallbackLocker.callbackRefs[subscriber.HandlerID] = struct { + handler GooseReportCallback + subscriber *GooseSubscriber + }{ + handler: subscriber.Conf.ReportHandler, + subscriber: subscriber, + } + + C.simple_goose_subscriber_set_listener( + subscriber.subscriber, + C.uintptr_t(subscriber.HandlerID), + ) + C.GooseReceiver_addSubscriber(receiver.gooseReceiver, subscriber.subscriber) + + return receiver +} + +func (receiver *GooseReceiver) RemoveSubscriber(subscriber *GooseSubscriber) *GooseReceiver { + gooseCallbackLocker.Lock() + defer gooseCallbackLocker.Unlock() + + C.GooseReceiver_removeSubscriber(receiver.gooseReceiver, subscriber.subscriber) + delete(gooseCallbackLocker.callbackRefs, subscriber.HandlerID) + + return receiver +} + +func (receiver *GooseReceiver) SetInterfaceID(interfaceID string) *GooseReceiver { + tmp := C.CString(interfaceID) + defer C.free(unsafe.Pointer(tmp)) + C.GooseReceiver_setInterfaceId(receiver.gooseReceiver, tmp) + + return receiver +} + +func (receiver *GooseReceiver) GetInterfaceID() string { + return C.GoString(C.GooseReceiver_getInterfaceId(receiver.gooseReceiver)) +} + +func (receiver *GooseReceiver) Start() *GooseReceiver { + C.GooseReceiver_start(receiver.gooseReceiver) + + return receiver +} + +func (receiver *GooseReceiver) IsRunning() bool { + return bool(C.GooseReceiver_isRunning(receiver.gooseReceiver)) +} + +func (receiver *GooseReceiver) Tick() bool { + return bool(C.GooseReceiver_tick(receiver.gooseReceiver)) +} + +func (receiver *GooseReceiver) Stop() *GooseReceiver { + C.GooseReceiver_stop(receiver.gooseReceiver) + + return receiver +} + +func (receiver *GooseReceiver) Destroy() { + C.GooseReceiver_destroy(receiver.gooseReceiver) +} diff --git a/goose_subscriber.go b/goose_subscriber.go new file mode 100644 index 0000000..22cd67c --- /dev/null +++ b/goose_subscriber.go @@ -0,0 +1,172 @@ +package iec61850 + +/* +#include "goose_receiver.h" +#include "goose_subscriber.h" +#include + +// 创建 goose 订阅对象 +static GooseSubscriber create_simple_goose_subscriber(char *goCbRef) { + return GooseSubscriber_create(goCbRef, NULL); +} +*/ +import "C" + +import ( + "unsafe" +) + +type ( + GooseReportCallback func(report *GooseReport) + + SubscriberConf struct { + InterfaceID string + DstMacAddr [6]uint8 + AppID uint16 + Subscriber string + ReportHandler GooseReportCallback + } + + GooseSubscriber struct { + noCopy struct{} + Conf SubscriberConf // 网卡名称 + subscriber *C.struct_sGooseSubscriber + HandlerID GooseCallbackHandlerID + } + + GooseCallbackHandlerID uintptr +) + +func NewGooseSubscriber(conf SubscriberConf) (subscriber *GooseSubscriber) { + etherName := C.CString(conf.InterfaceID) + defer C.free(unsafe.Pointer(etherName)) + goCbRef := C.CString(conf.Subscriber) + defer C.free(unsafe.Pointer(goCbRef)) + + cSubscriber := C.create_simple_goose_subscriber(goCbRef) + C.GooseSubscriber_setDstMac(cSubscriber, (*C.uint8_t)(unsafe.Pointer(&conf.DstMacAddr[0]))) + C.GooseSubscriber_setAppId(cSubscriber, C.uint16_t(conf.AppID)) + newID := GooseCallbackHandlerID(gooseCallbackLocker.idOffset.Add(1)) + subscriber = &GooseSubscriber{ + subscriber: cSubscriber, + Conf: conf, + HandlerID: newID, + } + + return +} + +type ( + GooseReport struct { + parameter unsafe.Pointer + *GooseSubscriber + } + + GooseParseError int +) + +const ( + GooseParseErrorNoError GooseParseError = iota + GooseParseErrorUnknownTag + GooseParseErrorTagDecode + GooseParseErrorSublevel + GooseParseErrorOverflow + GooseParseErrorUnderflow + GooseParseErrorTypeMismatch + GooseParseErrorLengthMismatch + GooseParseErrorInvalidPadding +) + +func (receiver *GooseSubscriber) GetGoID() string { + return C.GoString(C.GooseSubscriber_getGoId(receiver.subscriber)) +} + +func (receiver *GooseSubscriber) GetGoCbRef() string { + return C.GoString(C.GooseSubscriber_getGoCbRef(receiver.subscriber)) +} + +func (receiver *GooseSubscriber) GetDataSetName() string { + return C.GoString(C.GooseSubscriber_getDataSet(receiver.subscriber)) +} + +func (receiver *GooseSubscriber) GetParseError() GooseParseError { + return GooseParseError(C.GooseSubscriber_getParseError(receiver.subscriber)) +} + +func (receiver *GooseSubscriber) IsValid() bool { + return bool(C.GooseSubscriber_isValid(receiver.subscriber)) +} + +func (receiver *GooseSubscriber) GetAppID() int32 { + return int32(C.GooseSubscriber_getAppId(receiver.subscriber)) +} + +func (receiver *GooseSubscriber) GetSrcMac() [6]uint8 { + bf := [6]byte{} + C.GooseSubscriber_getSrcMac(receiver.subscriber, (*C.uint8_t)(unsafe.Pointer(&bf[0]))) + return bf +} + +func (receiver *GooseSubscriber) GetDstMac() [6]uint8 { + bf := [6]byte{} + C.GooseSubscriber_getDstMac(receiver.subscriber, (*C.uint8_t)(unsafe.Pointer(&bf[0]))) + return bf +} + +func (receiver *GooseSubscriber) GetStNum() uint32 { + return uint32(C.GooseSubscriber_getStNum(receiver.subscriber)) +} + +func (receiver *GooseSubscriber) GetSqNum() uint32 { + return uint32(C.GooseSubscriber_getSqNum(receiver.subscriber)) +} + +func (receiver *GooseSubscriber) IsTest() bool { + return bool(C.GooseSubscriber_isTest(receiver.subscriber)) +} + +func (receiver *GooseSubscriber) GetConfRev() uint32 { + return uint32(C.GooseSubscriber_getConfRev(receiver.subscriber)) +} + +func (receiver *GooseSubscriber) NeedsCommission() bool { + return bool(C.GooseSubscriber_needsCommission(receiver.subscriber)) +} + +func (receiver *GooseSubscriber) GetTimeAllowedToLive() uint32 { + return uint32(C.GooseSubscriber_getTimeAllowedToLive(receiver.subscriber)) +} + +func (receiver *GooseSubscriber) GetTimestamp() uint64 { + return uint64(C.GooseSubscriber_getTimestamp(receiver.subscriber)) +} + +func (receiver *GooseSubscriber) IsVlanSet() bool { + return bool(C.GooseSubscriber_isVlanSet(receiver.subscriber)) +} + +func (receiver *GooseSubscriber) GetVlanID() uint16 { + return uint16(C.GooseSubscriber_getVlanId(receiver.subscriber)) +} + +func (receiver *GooseSubscriber) GetVlanPriority() uint8 { + return uint8(C.GooseSubscriber_getVlanPrio(receiver.subscriber)) +} + +func (receiver *GooseSubscriber) GetDataSetValues() (*MmsValue, error) { + cTypeMmsValue := C.GooseSubscriber_getDataSetValues(receiver.subscriber) + mmsType := MmsType(C.MmsValue_getType(cTypeMmsValue)) + if mmsValue, err := toGoValue(cTypeMmsValue, mmsType); err != nil { + return nil, err + } else { + return &MmsValue{ + Type: mmsType, + Value: mmsValue, + }, nil + } +} + +func (receiver *GooseSubscriber) Destroy() { + C.GooseSubscriber_destroy(receiver.subscriber) + receiver.subscriber = nil +} diff --git a/libiec61850/lib/win64/libhal.a b/libiec61850/lib/win64/libhal.a new file mode 100644 index 0000000..5736bcc Binary files /dev/null and b/libiec61850/lib/win64/libhal.a differ diff --git a/libiec61850/lib/win64/libiec61850.a b/libiec61850/lib/win64/libiec61850.a index 3be162f..7c45726 100755 Binary files a/libiec61850/lib/win64/libiec61850.a and b/libiec61850/lib/win64/libiec61850.a differ diff --git a/mms.go b/mms.go index dd8f78a..cee2764 100644 --- a/mms.go +++ b/mms.go @@ -4,8 +4,9 @@ package iec61850 import "C" import ( "fmt" - "github.com/spf13/cast" "unsafe" + + "github.com/spf13/cast" ) func toMmsValue(mmsType MmsType, value interface{}) (*C.MmsValue, error) { @@ -114,8 +115,8 @@ func toGoValue(mmsValue *C.MmsValue, mmsType MmsType) (interface{}, error) { } func toGoStructure(mmsValue *C.MmsValue, mmsType MmsType) ([]*MmsValue, error) { - if mmsType != Structure { - return nil, nil + if !(mmsType == Structure || mmsType == Array) { + return nil, fmt.Errorf("require struct or array type value, but got type code is: %d", mmsType) } mmsValues := make([]*MmsValue, 0) diff --git a/test/goose_publisher/goose_publisher_test.go b/test/goose_publisher/goose_publisher_test.go new file mode 100644 index 0000000..3ad869c --- /dev/null +++ b/test/goose_publisher/goose_publisher_test.go @@ -0,0 +1,71 @@ +package goose_publisher + +import ( + "os" + "os/signal" + "syscall" + "testing" + "time" + + "github.com/wendy512/iec61850" +) + +func TestGoosePublisher(t *testing.T) { + publisher, err := iec61850.NewGoosePublisher(iec61850.GoosePublisherConf{ + InterfaceID: "eth0", + AppID: 1000, + DstAddr: [6]uint8{ + 0x01, + 0x0c, + 0xcd, + 0x01, + 0x00, + 0x01, + }, + VlanID: 0, + VlanPriority: 4, + }) + if err != nil { + t.Fatal(err) + } + defer publisher.Close() + + publisher.SetGoCbRef("simpleIOGenericIO/LLN0$GO$gcbAnalogValues") + publisher.SetDataSetRef("simpleIOGenericIO/LLN0$AnalogValues") + publisher.SetConfRev(1) + publisher.SetTimeAllowedToLive(500) + + lVal := iec61850.NewLinkedListValue() + defer lVal.Destroy() + if err := lVal.Add(&iec61850.MmsValue{ + Type: iec61850.Int64, + Value: time.Now().UnixMilli(), + }); err != nil { + t.Fatal(err) + } + if err := lVal.Add(&iec61850.MmsValue{ + Type: iec61850.Float, + Value: 233.3, + }); err != nil { + t.Fatal(err) + } + t.Logf("linked list size is %d\n", lVal.Size()) + ticker := time.NewTicker(time.Second * 2) + defer ticker.Stop() + go func() { + var i uint64 + for range ticker.C { + if err := publisher.Publish(lVal); err != nil { + t.Error(err) + return + } else { + i++ + t.Logf("send [%d] goose message successfully", i) + } + } + }() + sig := make(chan os.Signal, 1) + signal.Notify(sig, syscall.SIGINT, syscall.SIGTERM) + <-sig + t.Logf("Goose Publisher Close\n") +} diff --git a/test/goose_subscriber/goose_subscriber_test.go b/test/goose_subscriber/goose_subscriber_test.go new file mode 100644 index 0000000..95c5833 --- /dev/null +++ b/test/goose_subscriber/goose_subscriber_test.go @@ -0,0 +1,53 @@ +package goose_subscriber + +import ( + "encoding/xml" + "fmt" + "os" + "os/signal" + "syscall" + "testing" + + "github.com/wendy512/iec61850" +) + +func printReportValue(report *iec61850.GooseReport) { + fmt.Printf("[appID: %d, goID: %s]\n", report.GetAppID(), report.GetGoID()) + fmt.Printf("[vlanID: %d, vlanPriority: %d]\n", report.GetVlanID(), report.GetVlanPriority()) + fmt.Printf("[dstMac: %#v, srcMac: %#v]\n", report.GetDstMac(), report.GetSrcMac()) + fmt.Printf("[goCbRef: %s, dataSetName: %s]\n", report.GetGoCbRef(), report.GetDataSetName()) + fmt.Printf("[timestamp: %d, time allowed to live: %d]\n", report.GetTimestamp(), report.GetTimeAllowedToLive()) + fmt.Printf("[stNum: %d, sqNum: %d]\n", report.GetStNum(), report.GetSqNum()) + + if v, err := report.GetDataSetValues(); err != nil { + fmt.Println("to GO value error: ", err) + } else { + str, _ := xml.MarshalIndent(v, "", " ") + fmt.Println(string(str)) + } +} + +func TestGooseSubscriber(t *testing.T) { + gooseReceiver := iec61850.NewGooseReceiver() + defer gooseReceiver.Stop().Destroy() + + subscriber1 := iec61850.NewGooseSubscriber(iec61850.SubscriberConf{ + InterfaceID: "eth0", + AppID: 1000, + DstMacAddr: [6]uint8{0x01, 0x0c, 0xcd, 0x01, 0x00, 0x01}, + Subscriber: "simpleIOGenericIO/LLN0$GO$gcbAnalogValues", + ReportHandler: printReportValue, + }) + + if !gooseReceiver. + AddSubscriber(subscriber1). + Start(). + IsRunning() { + t.Fatal("can't start goose receiver") + } + + sig := make(chan os.Signal, 1) + signal.Notify(sig, syscall.SIGINT, syscall.SIGTERM) + <-sig + t.Logf("Goose Subscriber Close\n") +} diff --git a/tls_config.go b/tls_config.go index 07604c8..1e33113 100644 --- a/tls_config.go +++ b/tls_config.go @@ -11,11 +11,11 @@ type TLSConfigVersion int const ( TLS_VERSION_NOT_SELECTED TLSConfigVersion = 0 - TLS_VERSION_SSL_3_0 = 3 - TLS_VERSION_TLS_1_0 = 4 - TLS_VERSION_TLS_1_1 = 5 - TLS_VERSION_TLS_1_2 = 6 - TLS_VERSION_TLS_1_3 = 7 + TLS_VERSION_SSL_3_0 TLSConfigVersion = 3 + TLS_VERSION_TLS_1_0 TLSConfigVersion = 4 + TLS_VERSION_TLS_1_1 TLSConfigVersion = 5 + TLS_VERSION_TLS_1_2 TLSConfigVersion = 6 + TLS_VERSION_TLS_1_3 TLSConfigVersion = 7 ) type TLSConfigurationEventHandler func(parameter unsafe.Pointer, eventLevel, eventCode int, message string, conn C.TLSConnection) diff --git a/types.go b/types.go index add49e5..1d8c0f3 100644 --- a/types.go +++ b/types.go @@ -38,21 +38,21 @@ type MmsDataAccessError int const ( DATA_ACCESS_ERROR_SUCCESS_NO_UPDATE MmsDataAccessError = -3 - DATA_ACCESS_ERROR_NO_RESPONSE = -2 - DATA_ACCESS_ERROR_SUCCESS = -1 - DATA_ACCESS_ERROR_OBJECT_INVALIDATED = 0 - DATA_ACCESS_ERROR_HARDWARE_FAULT = 1 - DATA_ACCESS_ERROR_TEMPORARILY_UNAVAILABLE = 2 - DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED = 3 - DATA_ACCESS_ERROR_OBJECT_UNDEFINED = 4 - DATA_ACCESS_ERROR_INVALID_ADDRESS = 5 - DATA_ACCESS_ERROR_TYPE_UNSUPPORTED = 6 - DATA_ACCESS_ERROR_TYPE_INCONSISTENT = 7 - DATA_ACCESS_ERROR_OBJECT_ATTRIBUTE_INCONSISTENT = 8 - DATA_ACCESS_ERROR_OBJECT_ACCESS_UNSUPPORTED = 9 - DATA_ACCESS_ERROR_OBJECT_NONE_EXISTENT = 10 - DATA_ACCESS_ERROR_OBJECT_VALUE_INVALID = 11 - DATA_ACCESS_ERROR_UNKNOWN = 12 + DATA_ACCESS_ERROR_NO_RESPONSE MmsDataAccessError = -2 + DATA_ACCESS_ERROR_SUCCESS MmsDataAccessError = -1 + DATA_ACCESS_ERROR_OBJECT_INVALIDATED MmsDataAccessError = 0 + DATA_ACCESS_ERROR_HARDWARE_FAULT MmsDataAccessError = 1 + DATA_ACCESS_ERROR_TEMPORARILY_UNAVAILABLE MmsDataAccessError = 2 + DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED MmsDataAccessError = 3 + DATA_ACCESS_ERROR_OBJECT_UNDEFINED MmsDataAccessError = 4 + DATA_ACCESS_ERROR_INVALID_ADDRESS MmsDataAccessError = 5 + DATA_ACCESS_ERROR_TYPE_UNSUPPORTED MmsDataAccessError = 6 + DATA_ACCESS_ERROR_TYPE_INCONSISTENT MmsDataAccessError = 7 + DATA_ACCESS_ERROR_OBJECT_ATTRIBUTE_INCONSISTENT MmsDataAccessError = 8 + DATA_ACCESS_ERROR_OBJECT_ACCESS_UNSUPPORTED MmsDataAccessError = 9 + DATA_ACCESS_ERROR_OBJECT_NONE_EXISTENT MmsDataAccessError = 10 + DATA_ACCESS_ERROR_OBJECT_VALUE_INVALID MmsDataAccessError = 11 + DATA_ACCESS_ERROR_UNKNOWN MmsDataAccessError = 12 ) type ControlHandlerResult int