diff --git a/plc4go/internal/ads/Browser.go b/plc4go/internal/ads/Browser.go index dfbee478eee..e78616a75d4 100644 --- a/plc4go/internal/ads/Browser.go +++ b/plc4go/internal/ads/Browser.go @@ -23,6 +23,7 @@ import ( "context" "strings" + "github.com/apache/plc4x/plc4go/internal/ads/model" apiModel "github.com/apache/plc4x/plc4go/pkg/api/model" driverModel "github.com/apache/plc4x/plc4go/protocols/ads/readwrite/model" internalModel "github.com/apache/plc4x/plc4go/spi/model" @@ -113,9 +114,9 @@ func (m *Connection) filterDataTypes(parentName string, currentType driverModel. }) } foundTag := &internalModel.DefaultPlcBrowseItem{ - Tag: SymbolicPlcTag{ - PlcTag: PlcTag{ - arrayInfo: arrayInfo, + Tag: model.SymbolicPlcTag{ + PlcTag: model.PlcTag{ + ArrayInfo: arrayInfo, }, SymbolicAddress: parentName, }, diff --git a/plc4go/internal/ads/Connection.go b/plc4go/internal/ads/Connection.go index b40f94e50da..9c3214b6d94 100644 --- a/plc4go/internal/ads/Connection.go +++ b/plc4go/internal/ads/Connection.go @@ -26,6 +26,7 @@ import ( "strconv" "strings" + model2 "github.com/apache/plc4x/plc4go/internal/ads/model" "github.com/apache/plc4x/plc4go/pkg/api" apiModel "github.com/apache/plc4x/plc4go/pkg/api/model" "github.com/apache/plc4x/plc4go/pkg/api/values" @@ -45,12 +46,14 @@ type Connection struct { messageCodec spi.MessageCodec requestInterceptor interceptors.RequestInterceptor - configuration Configuration + configuration model2.Configuration driverContext *DriverContext tracer *spi.Tracer + + subscriptions map[uint32]apiModel.PlcSubscriptionHandle } -func NewConnection(messageCodec spi.MessageCodec, configuration Configuration, options map[string][]string) (*Connection, error) { +func NewConnection(messageCodec spi.MessageCodec, configuration model2.Configuration, options map[string][]string) (*Connection, error) { driverContext, err := NewDriverContext(configuration) if err != nil { return nil, err @@ -59,6 +62,7 @@ func NewConnection(messageCodec spi.MessageCodec, configuration Configuration, o messageCodec: messageCodec, configuration: configuration, driverContext: driverContext, + subscriptions: map[uint32]apiModel.PlcSubscriptionHandle{}, } if traceEnabledOption, ok := options["traceEnabled"]; ok { if len(traceEnabledOption) == 1 { @@ -150,20 +154,54 @@ func (m *Connection) setupConnection(ctx context.Context, ch chan plc4go.PlcConn return } + // Start the worker for handling incoming messages + // (Messages that are not responses to outgoing messages) + defaultIncomingMessageChannel := m.messageCodec.GetDefaultIncomingMessageChannel() + go func() { + for message := range defaultIncomingMessageChannel { + switch message.(type) { + case model.AmsTCPPacket: + amsTCPPacket := message.(model.AmsTCPPacket) + switch amsTCPPacket.GetUserdata().(type) { + // Forward all device notification requests to the subscriber component. + case model.AdsDeviceNotificationRequest: + m.handleIncomingDeviceNotificationRequest( + amsTCPPacket.GetUserdata().(model.AdsDeviceNotificationRequest)) + default: + log.Warn().Msgf("Got unexpected type of incoming ADS message %v", message) + } + default: + log.Warn().Msgf("Got unexpected type of incoming ADS message %v", message) + } + } + }() + // Subscribe for changes to the symbol or the offline-versions - /*versionChangeRequest, err := m.SubscriptionRequestBuilder(). + versionChangeRequest, err := m.SubscriptionRequestBuilder(). AddChangeOfStateTagAddress("offlineVersion", "0xF008/0x0000:USINT"). AddPreRegisteredConsumer("offlineVersion", func(event apiModel.PlcSubscriptionEvent) { - err := m.readSymbolTableAndDatatypeTable(ctx) - if err != nil { - log.Error().Err(err).Msg("error updating data-type and symbol tables") + if event.GetResponseCode("offlineVersion") == apiModel.PlcResponseCode_OK { + newVersion := event.GetValue("offlineVersion").GetUint8() + if newVersion != m.driverContext.symbolVersion { + log.Info().Msg("detected offline version change: reloading symbol- and data-type-table.") + err := m.readSymbolTableAndDatatypeTable(ctx) + if err != nil { + log.Error().Err(err).Msg("error updating data-type and symbol tables") + } + } } }). AddChangeOfStateTagAddress("onlineVersion", "TwinCAT_SystemInfoVarList._AppInfo.OnlineChangeCnt"). AddPreRegisteredConsumer("onlineVersion", func(event apiModel.PlcSubscriptionEvent) { - err := m.readSymbolTableAndDatatypeTable(ctx) - if err != nil { - log.Error().Err(err).Msg("error updating data-type and symbol tables") + if event.GetResponseCode("onlineVersion") == apiModel.PlcResponseCode_OK { + newVersion := event.GetValue("onlineVersion").GetUint32() + if newVersion != m.driverContext.onlineVersion { + log.Info().Msg("detected online version change: reloading symbol- and data-type-table.") + err := m.readSymbolTableAndDatatypeTable(ctx) + if err != nil { + log.Error().Err(err).Msg("error updating data-type and symbol tables") + } + } } }). Build() @@ -172,7 +210,7 @@ func (m *Connection) setupConnection(ctx context.Context, ch chan plc4go.PlcConn if subscriptionRequestResult.GetErr() != nil { ch <- _default.NewDefaultPlcConnectionCloseResult(nil, subscriptionRequestResult.GetErr()) return - }*/ + } // Return the finished connection ch <- _default.NewDefaultPlcConnectionConnectResult(m, nil) @@ -251,7 +289,7 @@ func (m *Connection) readSymbolTable(ctx context.Context, symbolTableSize uint32 return symbols, nil } -func (m *Connection) resolveSymbolicTag(ctx context.Context, symbolicTag SymbolicPlcTag) (*DirectPlcTag, error) { +func (m *Connection) resolveSymbolicTag(ctx context.Context, symbolicTag model2.SymbolicPlcTag) (*model2.DirectPlcTag, error) { // Find the initial datatype, based on the first to segments. symbolicAddress := symbolicTag.SymbolicAddress addressParts := strings.Split(symbolicAddress, ".") @@ -278,7 +316,7 @@ func (m *Connection) resolveSymbolicTag(ctx context.Context, symbolicTag Symboli return m.resolveSymbolicAddress(ctx, addressParts, dataType, symbol.GetGroup(), symbol.GetOffset()) } -func (m *Connection) resolveSymbolicAddress(ctx context.Context, addressParts []string, curDataType model.AdsDataTypeTableEntry, indexGroup uint32, indexOffset uint32) (*DirectPlcTag, error) { +func (m *Connection) resolveSymbolicAddress(ctx context.Context, addressParts []string, curDataType model.AdsDataTypeTableEntry, indexGroup uint32, indexOffset uint32) (*model2.DirectPlcTag, error) { // If we've reached then end of the resolution, return the final entry. if len(addressParts) == 0 { var arrayInfo []apiModel.ArrayInfo @@ -289,9 +327,9 @@ func (m *Connection) resolveSymbolicAddress(ctx context.Context, addressParts [] }) } plcValueType, stringLength := m.getPlcValueForAdsDataTypeTableEntry(curDataType) - return &DirectPlcTag{ - PlcTag: PlcTag{ - arrayInfo: arrayInfo, + return &model2.DirectPlcTag{ + PlcTag: model2.PlcTag{ + ArrayInfo: arrayInfo, }, IndexGroup: indexGroup, IndexOffset: indexOffset, diff --git a/plc4go/internal/ads/Driver.go b/plc4go/internal/ads/Driver.go index 687caf1db96..ac657b7345c 100644 --- a/plc4go/internal/ads/Driver.go +++ b/plc4go/internal/ads/Driver.go @@ -24,6 +24,7 @@ import ( "net/url" "strconv" + "github.com/apache/plc4x/plc4go/internal/ads/model" "github.com/apache/plc4x/plc4go/pkg/api" apiModel "github.com/apache/plc4x/plc4go/pkg/api/model" adsModel "github.com/apache/plc4x/plc4go/protocols/ads/readwrite/model" @@ -71,7 +72,7 @@ func (m *Driver) GetConnection(transportUrl url.URL, transports map[string]trans codec := NewMessageCodec(transportInstance) log.Debug().Msgf("working with codec %#v", codec) - configuration, err := ParseFromOptions(options) + configuration, err := model.ParseFromOptions(options) if err != nil { log.Error().Err(err).Msgf("Invalid options") ch := make(chan plc4go.PlcConnectionConnectResult) diff --git a/plc4go/internal/ads/DriverContext.go b/plc4go/internal/ads/DriverContext.go index 4276ad137d0..edae1f40655 100644 --- a/plc4go/internal/ads/DriverContext.go +++ b/plc4go/internal/ads/DriverContext.go @@ -26,6 +26,7 @@ import ( "strings" "sync/atomic" + model3 "github.com/apache/plc4x/plc4go/internal/ads/model" "github.com/apache/plc4x/plc4go/pkg/api/model" "github.com/apache/plc4x/plc4go/pkg/api/values" driverModel "github.com/apache/plc4x/plc4go/protocols/ads/readwrite/model" @@ -45,7 +46,7 @@ type DriverContext struct { awaitDisconnectComplete bool } -func NewDriverContext(configuration Configuration) (*DriverContext, error) { +func NewDriverContext(configuration model3.Configuration) (*DriverContext, error) { return &DriverContext{ invokeId: 0, }, nil @@ -64,7 +65,7 @@ func (m *DriverContext) clear() { m.awaitDisconnectComplete = false } -func (m *DriverContext) getDirectTagForSymbolTag(symbolicPlcTag SymbolicPlcTag) (*DirectPlcTag, error) { +func (m *DriverContext) getDirectTagForSymbolTag(symbolicPlcTag model3.SymbolicPlcTag) (*model3.DirectPlcTag, error) { address := symbolicPlcTag.SymbolicAddress addressSegments := strings.Split(address, ".") var symbolName string @@ -90,16 +91,16 @@ func (m *DriverContext) getDirectTagForSymbolTag(symbolicPlcTag SymbolicPlcTag) return m.resolveDirectTag(remainingSegments, dataTypeEntry, symbolEntry.GetGroup(), symbolEntry.GetOffset()) } -func (m *DriverContext) resolveDirectTag(remainingSegments []string, currentDatatype driverModel.AdsDataTypeTableEntry, indexGroup uint32, indexOffset uint32) (*DirectPlcTag, error) { +func (m *DriverContext) resolveDirectTag(remainingSegments []string, currentDatatype driverModel.AdsDataTypeTableEntry, indexGroup uint32, indexOffset uint32) (*model3.DirectPlcTag, error) { if len(remainingSegments) == 0 { - return &DirectPlcTag{ + return &model3.DirectPlcTag{ IndexGroup: indexGroup, IndexOffset: indexOffset, ValueType: m.getDataTypeForDataTypeTableEntry(currentDatatype), StringLength: m.getStringLengthForDataTypeTableEntry(currentDatatype), DataType: currentDatatype, - PlcTag: PlcTag{ - arrayInfo: m.getArrayInfoForDataTypeTableEntry(currentDatatype), + PlcTag: model3.PlcTag{ + ArrayInfo: m.getArrayInfoForDataTypeTableEntry(currentDatatype), }, }, nil } diff --git a/plc4go/internal/ads/MessageTemplates.go b/plc4go/internal/ads/MessageTemplates.go index 6f77f4bbed0..369b207ae1b 100644 --- a/plc4go/internal/ads/MessageTemplates.go +++ b/plc4go/internal/ads/MessageTemplates.go @@ -23,24 +23,24 @@ import adsModel "github.com/apache/plc4x/plc4go/protocols/ads/readwrite/model" func (m *Connection) NewAdsReadDeviceInfoRequest() adsModel.AmsTCPPacket { return adsModel.NewAmsTCPPacket( - adsModel.NewAdsReadDeviceInfoRequest(m.configuration.targetAmsNetId, uint16(adsModel.DefaultAmsPorts_RUNTIME_SYSTEM_01), + adsModel.NewAdsReadDeviceInfoRequest(m.configuration.TargetAmsNetId, uint16(adsModel.DefaultAmsPorts_RUNTIME_SYSTEM_01), // TODO: Replace 800 with constant. - m.configuration.sourceAmsNetId, 800, 0, m.driverContext.getInvokeId())) + m.configuration.SourceAmsNetId, 800, 0, m.driverContext.getInvokeId())) } func (m *Connection) NewAdsReadRequest(indexGroup uint32, indexOffset uint32, length uint32) adsModel.AmsTCPPacket { return adsModel.NewAmsTCPPacket( adsModel.NewAdsReadRequest(indexGroup, indexOffset, length, - m.configuration.targetAmsNetId, m.configuration.targetAmsPort, - m.configuration.sourceAmsNetId, m.configuration.sourceAmsPort, 0, m.driverContext.getInvokeId())) + m.configuration.TargetAmsNetId, m.configuration.TargetAmsPort, + m.configuration.SourceAmsNetId, m.configuration.SourceAmsPort, 0, m.driverContext.getInvokeId())) } func (m *Connection) NewAdsWriteRequest(indexGroup uint32, indexOffset uint32, data []byte) adsModel.AmsTCPPacket { return adsModel.NewAmsTCPPacket( adsModel.NewAdsWriteRequest( indexGroup, indexOffset, data, - m.configuration.targetAmsNetId, m.configuration.targetAmsPort, - m.configuration.sourceAmsNetId, m.configuration.sourceAmsPort, + m.configuration.TargetAmsNetId, m.configuration.TargetAmsPort, + m.configuration.SourceAmsNetId, m.configuration.SourceAmsPort, 0, m.driverContext.getInvokeId())) } @@ -48,8 +48,8 @@ func (m *Connection) NewAdsReadWriteRequest(indexGroup uint32, indexOffset uint3 return adsModel.NewAmsTCPPacket( adsModel.NewAdsReadWriteRequest( indexGroup, indexOffset, readLength, items, writeData, - m.configuration.targetAmsNetId, m.configuration.targetAmsPort, - m.configuration.sourceAmsNetId, m.configuration.sourceAmsPort, + m.configuration.TargetAmsNetId, m.configuration.TargetAmsPort, + m.configuration.SourceAmsNetId, m.configuration.SourceAmsPort, 0, m.driverContext.getInvokeId())) } @@ -57,8 +57,8 @@ func (m *Connection) NewAdsAddDeviceNotificationRequest(indexGroup uint32, index return adsModel.NewAmsTCPPacket( adsModel.NewAdsAddDeviceNotificationRequest( indexGroup, indexOffset, length, transmissionMode, maxDelay, cycleTime, - m.configuration.targetAmsNetId, m.configuration.targetAmsPort, - m.configuration.sourceAmsNetId, m.configuration.sourceAmsPort, + m.configuration.TargetAmsNetId, m.configuration.TargetAmsPort, + m.configuration.SourceAmsNetId, m.configuration.SourceAmsPort, 0, m.driverContext.getInvokeId())) } @@ -66,7 +66,7 @@ func (m *Connection) NewAdsDeleteDeviceNotificationRequest(notificationHandle ui return adsModel.NewAmsTCPPacket( adsModel.NewAdsDeleteDeviceNotificationRequest( notificationHandle, - m.configuration.targetAmsNetId, m.configuration.targetAmsPort, - m.configuration.sourceAmsNetId, m.configuration.sourceAmsPort, + m.configuration.TargetAmsNetId, m.configuration.TargetAmsPort, + m.configuration.SourceAmsNetId, m.configuration.SourceAmsPort, 0, m.driverContext.getInvokeId())) } diff --git a/plc4go/internal/ads/Reader.go b/plc4go/internal/ads/Reader.go index 7494ad50c9f..9b4f938fe4e 100644 --- a/plc4go/internal/ads/Reader.go +++ b/plc4go/internal/ads/Reader.go @@ -25,6 +25,7 @@ import ( "fmt" "strings" + "github.com/apache/plc4x/plc4go/internal/ads/model" apiModel "github.com/apache/plc4x/plc4go/pkg/api/model" "github.com/apache/plc4x/plc4go/pkg/api/values" driverModel "github.com/apache/plc4x/plc4go/protocols/ads/readwrite/model" @@ -66,8 +67,8 @@ func (m *Connection) singleRead(ctx context.Context, readRequest apiModel.PlcRea // Here we can be sure that we're only handling a single request. tagName := readRequest.GetTagNames()[0] tag := readRequest.GetTag(tagName) - if needsResolving(tag) { - adsField, err := castToSymbolicPlcTagFromPlcTag(tag) + if model.NeedsResolving(tag) { + adsField, err := model.CastToSymbolicPlcTagFromPlcTag(tag) if err != nil { result <- &internalModel.DefaultPlcReadRequestResult{ Request: readRequest, @@ -89,7 +90,7 @@ func (m *Connection) singleRead(ctx context.Context, readRequest apiModel.PlcRea return } } - directAdsTag, ok := tag.(*DirectPlcTag) + directAdsTag, ok := tag.(*model.DirectPlcTag) if !ok { result <- &internalModel.DefaultPlcReadRequestResult{ Request: readRequest, @@ -142,12 +143,12 @@ func (m *Connection) multiRead(ctx context.Context, readRequest apiModel.PlcRead // Calculate the size of all tags together. // Calculate the expected size of the response data. expectedResponseDataSize := uint32(0) - directAdsTags := map[string]*DirectPlcTag{} + directAdsTags := map[string]*model.DirectPlcTag{} requestItems := make([]driverModel.AdsMultiRequestItem, 0) for _, tagName := range readRequest.GetTagNames() { tag := readRequest.GetTag(tagName) - if needsResolving(tag) { - adsField, err := castToSymbolicPlcTagFromPlcTag(tag) + if model.NeedsResolving(tag) { + adsField, err := model.CastToSymbolicPlcTagFromPlcTag(tag) if err != nil { result <- &internalModel.DefaultPlcReadRequestResult{ Request: readRequest, @@ -169,7 +170,7 @@ func (m *Connection) multiRead(ctx context.Context, readRequest apiModel.PlcRead return } } - directAdsTag, ok := tag.(*DirectPlcTag) + directAdsTag, ok := tag.(*model.DirectPlcTag) if !ok { result <- &internalModel.DefaultPlcReadRequestResult{ Request: readRequest, diff --git a/plc4go/internal/ads/Subscriber.go b/plc4go/internal/ads/Subscriber.go index 9921354b0b6..92400449365 100644 --- a/plc4go/internal/ads/Subscriber.go +++ b/plc4go/internal/ads/Subscriber.go @@ -21,9 +21,15 @@ package ads import ( "context" + "time" + dirverModel "github.com/apache/plc4x/plc4go/internal/ads/model" apiModel "github.com/apache/plc4x/plc4go/pkg/api/model" + "github.com/apache/plc4x/plc4go/protocols/ads/readwrite/model" internalModel "github.com/apache/plc4x/plc4go/spi/model" + "github.com/apache/plc4x/plc4go/spi/utils" + "github.com/pkg/errors" + "github.com/rs/zerolog/log" ) func (m *Connection) SubscriptionRequestBuilder() apiModel.PlcSubscriptionRequestBuilder { @@ -36,8 +42,191 @@ func (m *Connection) UnsubscriptionRequestBuilder() apiModel.PlcUnsubscriptionRe } func (m *Connection) Subscribe(ctx context.Context, subscriptionRequest apiModel.PlcSubscriptionRequest) <-chan apiModel.PlcSubscriptionRequestResult { - // Unfortunately the subscription API is the only part that doesn't support multi-item requests. - return nil + defaultSubscriptionRequest := subscriptionRequest.(*internalModel.DefaultPlcSubscriptionRequest) + + // Subscription requests are unfortunately now supported as multi-item requests, + // so we have to send one subscription request for every tag. + // Prepare the subscription requests, by ensuring any symbolic tags are correctly resolved + // and splitting them up into single-tag requests. + subSubscriptionRequests := map[string]apiModel.PlcSubscriptionRequest{} + subResults := map[string]apiModel.PlcSubscriptionRequestResult{} + // Iterate over all requests and add the result-channels to the list + for _, tagName := range subscriptionRequest.GetTagNames() { + tag := subscriptionRequest.GetTag(tagName) + var directTag dirverModel.DirectPlcTag + switch tag.(type) { + case dirverModel.DirectPlcTag: + directTag = tag.(dirverModel.DirectPlcTag) + case dirverModel.SymbolicPlcTag: + symbolicTag := tag.(dirverModel.SymbolicPlcTag) + directTagPtr, err := m.driverContext.getDirectTagForSymbolTag(symbolicTag) + if err != nil { + subResults[tagName] = &internalModel.DefaultPlcSubscriptionRequestResult{ + Request: nil, Err: errors.Wrap(err, "error resolving symbolic tag")} + continue + } + directTag = *directTagPtr + default: + subResults[tagName] = &internalModel.DefaultPlcSubscriptionRequestResult{ + Request: nil, Err: errors.New("invalid tag type")} + continue + } + + subscriptionType := defaultSubscriptionRequest.GetType(tagName) + interval := defaultSubscriptionRequest.GetInterval(tagName) + subSubscriptionRequests[tagName] = internalModel.NewDefaultPlcSubscriptionRequest(m, + []string{tagName}, + map[string]apiModel.PlcTag{tagName: directTag}, + map[string]internalModel.SubscriptionType{tagName: subscriptionType}, + map[string]time.Duration{tagName: interval}, + map[string][]apiModel.PlcSubscriptionEventConsumer{tagName: {}}) + } + + // If this is a single item request, we can take a shortcut. + if len(subSubscriptionRequests) == 1 { + tagName := subscriptionRequest.GetTagNames()[0] + subSubscriptionRequest := subSubscriptionRequests[tagName] + return m.subscribe(ctx, subSubscriptionRequest) + } + + // Create a sub-result-channel map and prepare execute the subscriptions. + subResultChannels := map[string]<-chan apiModel.PlcSubscriptionRequestResult{} + for tagName, subSubscriptionRequest := range subSubscriptionRequests { + subResultChannels[tagName] = m.subscribe(ctx, subSubscriptionRequest) + } + + // Create a new result-channel, which completes as soon as all sub-result-channels have returned + globalResultChannel := make(chan apiModel.PlcSubscriptionRequestResult) + go func() { + // Iterate over all sub-results + for _, subResultChannel := range subResultChannels { + select { + case <-ctx.Done(): + globalResultChannel <- &internalModel.DefaultPlcSubscriptionRequestResult{Request: subscriptionRequest, Err: ctx.Err()} + return + case subResult := <-subResultChannel: + // These are all single value requests ... so it's safe to assume this shortcut. + tagName := subResult.GetRequest().GetTagNames()[0] + subResults[tagName] = subResult + } + } + // As soon as all are done, process the results + result := m.processSubscriptionResponses(ctx, subscriptionRequest, subResults) + // Return the final result + globalResultChannel <- result + }() + + return globalResultChannel +} + +func (m *Connection) subscribe(ctx context.Context, subscriptionRequest apiModel.PlcSubscriptionRequest) <-chan apiModel.PlcSubscriptionRequestResult { + responseChan := make(chan apiModel.PlcSubscriptionRequestResult) + go func(respChan chan apiModel.PlcSubscriptionRequestResult) { + // At this point we are sure to only have single item direct tag requests. + tagName := subscriptionRequest.GetTagNames()[0] + directTag := subscriptionRequest.GetTag(tagName).(dirverModel.DirectPlcTag) + if directTag.DataType == nil { + directTag.DataType = m.driverContext.dataTypeTable[directTag.ValueType.String()] + } + + response, err := m.ExecuteAdsAddDeviceNotificationRequest(ctx, directTag.IndexGroup, directTag.IndexOffset, directTag.DataType.GetSize(), model.AdsTransMode_ON_CHANGE, 0, 0) + if err != nil { + respChan <- &internalModel.DefaultPlcSubscriptionRequestResult{ + Request: subscriptionRequest, + Response: nil, + Err: err, + } + } + // Create a new subscription handle. + subscriptionHandle := dirverModel.NewAdsSubscriptionHandle(m, tagName, directTag) + respChan <- &internalModel.DefaultPlcSubscriptionRequestResult{ + Request: subscriptionRequest, + Response: internalModel.NewDefaultPlcSubscriptionResponse(subscriptionRequest, + map[string]apiModel.PlcResponseCode{tagName: apiModel.PlcResponseCode_OK}, + map[string]apiModel.PlcSubscriptionHandle{tagName: subscriptionHandle}), + } + // Store it together with the returned ADS handle. + m.subscriptions[response.GetNotificationHandle()] = subscriptionHandle + }(responseChan) + return responseChan +} + +func (m *Connection) processSubscriptionResponses(_ context.Context, subscriptionRequest apiModel.PlcSubscriptionRequest, subscriptionResults map[string]apiModel.PlcSubscriptionRequestResult) apiModel.PlcSubscriptionRequestResult { + if len(subscriptionResults) == 1 { + log.Debug().Msg("We got only one response, no merging required") + for tagName := range subscriptionResults { + return subscriptionResults[tagName] + } + } + + log.Trace().Msg("Merging requests") + responseCodes := map[string]apiModel.PlcResponseCode{} + subscriptionHandles := map[string]apiModel.PlcSubscriptionHandle{} + var err error = nil + for _, subscriptionResult := range subscriptionResults { + if subscriptionResult.GetErr() != nil { + log.Debug().Err(subscriptionResult.GetErr()).Msgf("Error during subscription") + if err == nil { + // Lazy initialization of multi error + err = utils.MultiError{MainError: errors.New("while aggregating results"), Errors: []error{subscriptionResult.GetErr()}} + } else { + multiError := err.(utils.MultiError) + multiError.Errors = append(multiError.Errors, subscriptionResult.GetErr()) + } + } else if subscriptionResult.GetResponse() != nil { + if len(subscriptionResult.GetResponse().GetRequest().GetTagNames()) > 1 { + log.Error().Int("numberOfTags", len(subscriptionResult.GetResponse().GetRequest().GetTagNames())).Msg("We should only get 1") + } + for _, tagName := range subscriptionResult.GetResponse().GetRequest().GetTagNames() { + handle, err := subscriptionResult.GetResponse().GetSubscriptionHandle(tagName) + if err != nil { + responseCodes[tagName] = apiModel.PlcResponseCode_REMOTE_ERROR + } else { + responseCodes[tagName] = subscriptionResult.GetResponse().GetResponseCode(tagName) + subscriptionHandles[tagName] = handle + } + } + } + } + return &internalModel.DefaultPlcSubscriptionRequestResult{ + Request: subscriptionRequest, + Response: internalModel.NewDefaultPlcSubscriptionResponse(subscriptionRequest, responseCodes, subscriptionHandles), + Err: err, + } +} + +func (m *Connection) handleIncomingDeviceNotificationRequest(deviceNotificationRequest model.AdsDeviceNotificationRequest) { + for _, stampHeader := range deviceNotificationRequest.GetAdsStampHeaders() { + for _, sample := range stampHeader.GetAdsNotificationSamples() { + notificationHandle := sample.GetNotificationHandle() + subscriptionHandler, ok := m.subscriptions[notificationHandle] + if !ok { + continue + } + adsSubscriptionHandler, ok := subscriptionHandler.(*dirverModel.AdsSubscriptionHandle) + if !ok { + continue + } + + // If no one is consuming, we don't need to waste effort in parsing. + if adsSubscriptionHandler.GetNumConsumers() == 0 { + continue + } + + // Create a readBuffer containing the sample data + readBuffer := utils.NewReadBufferByteBased(sample.GetData()) + + // Parse the data according to the type data stored in the tag + directTag := adsSubscriptionHandler.GetDirectTag() + plcValue, err := m.parsePlcValue(directTag.DataType, directTag.DataType.GetArrayInfo(), readBuffer) + if err != nil { + continue + } + + // Publish the parsed value to the subscribers. + adsSubscriptionHandler.PublishPlcValue(plcValue) + } + } } func (m *Connection) Unsubscribe(ctx context.Context, unsubscriptionRequest apiModel.PlcUnsubscriptionRequest) <-chan apiModel.PlcUnsubscriptionRequestResult { diff --git a/plc4go/internal/ads/TagHandler.go b/plc4go/internal/ads/TagHandler.go index c23b7c99681..70c1ba17845 100644 --- a/plc4go/internal/ads/TagHandler.go +++ b/plc4go/internal/ads/TagHandler.go @@ -27,6 +27,7 @@ import ( "strconv" "strings" + "github.com/apache/plc4x/plc4go/internal/ads/model" apiModel "github.com/apache/plc4x/plc4go/pkg/api/model" "github.com/apache/plc4x/plc4go/pkg/api/values" model2 "github.com/apache/plc4x/plc4go/spi/model" @@ -110,7 +111,7 @@ func (m TagHandler) ParseTag(query string) (apiModel.PlcTag, error) { return nil, fmt.Errorf("invalid ads data type") } - stringLength := NONE + stringLength := model.NONE var arrayInfo []apiModel.ArrayInfo tmpStringLength, err := strconv.ParseInt(match["stringLength"], 10, 32) @@ -170,7 +171,7 @@ func (m TagHandler) ParseTag(query string) (apiModel.PlcTag, error) { } } - return newDirectAdsPlcTag(indexGroup, indexOffset, plcValueType, stringLength, arrayInfo) + return model.NewDirectAdsPlcTag(indexGroup, indexOffset, plcValueType, stringLength, arrayInfo) } else if match := utils.GetSubgroupMatches(m.directAdsTag, query); match != nil { var indexGroup uint32 if indexGroupHexString := match["indexGroupHex"]; indexGroupHexString != "" { @@ -270,7 +271,7 @@ func (m TagHandler) ParseTag(query string) (apiModel.PlcTag, error) { } } - return newDirectAdsPlcTag(indexGroup, indexOffset, plcValueType, NONE, arrayInfo) + return model.NewDirectAdsPlcTag(indexGroup, indexOffset, plcValueType, model.NONE, arrayInfo) } else if match := utils.GetSubgroupMatches(m.symbolicAdsTag, query); match != nil { var arrayInfo []apiModel.ArrayInfo @@ -325,7 +326,7 @@ func (m TagHandler) ParseTag(query string) (apiModel.PlcTag, error) { } } - return newAdsSymbolicPlcTag(match["symbolicAddress"], arrayInfo) + return model.NewAdsSymbolicPlcTag(match["symbolicAddress"], arrayInfo) } else { return nil, errors.Errorf("Invalid address format for address '%s'", query) } diff --git a/plc4go/internal/ads/TagHandler_test.go b/plc4go/internal/ads/TagHandler_test.go index f2850dcfc94..c9ce4ff5fcf 100644 --- a/plc4go/internal/ads/TagHandler_test.go +++ b/plc4go/internal/ads/TagHandler_test.go @@ -24,6 +24,7 @@ import ( "regexp" "testing" + model2 "github.com/apache/plc4x/plc4go/internal/ads/model" "github.com/apache/plc4x/plc4go/pkg/api/model" "github.com/apache/plc4x/plc4go/pkg/api/values" model3 "github.com/apache/plc4x/plc4go/spi/model" @@ -51,11 +52,11 @@ func TestTagHandler_ParseQuery(t *testing.T) { args: args{ query: "1234/5678:BOOL", }, - want: DirectPlcTag{ + want: model2.DirectPlcTag{ IndexGroup: 1234, IndexOffset: 5678, ValueType: values.BOOL, - StringLength: NONE, + StringLength: model2.NONE, }, }, { @@ -63,11 +64,11 @@ func TestTagHandler_ParseQuery(t *testing.T) { args: args{ query: "0x04D2/0x162E:BOOL", }, - want: DirectPlcTag{ + want: model2.DirectPlcTag{ IndexGroup: 1234, IndexOffset: 5678, ValueType: values.BOOL, - StringLength: NONE, + StringLength: model2.NONE, }, }, { @@ -75,7 +76,7 @@ func TestTagHandler_ParseQuery(t *testing.T) { args: args{ query: "1234/5678:STRING(80)", }, - want: DirectPlcTag{ + want: model2.DirectPlcTag{ IndexGroup: 1234, IndexOffset: 5678, ValueType: values.STRING, @@ -87,7 +88,7 @@ func TestTagHandler_ParseQuery(t *testing.T) { args: args{ query: "0x04D2/0x162E:WSTRING(80)", }, - want: DirectPlcTag{ + want: model2.DirectPlcTag{ IndexGroup: 1234, IndexOffset: 5678, ValueType: values.WSTRING, @@ -99,7 +100,7 @@ func TestTagHandler_ParseQuery(t *testing.T) { args: args{ query: "MAIN.testVariable", }, - want: SymbolicPlcTag{ + want: model2.SymbolicPlcTag{ SymbolicAddress: "MAIN.testVariable", }, }, @@ -109,9 +110,9 @@ func TestTagHandler_ParseQuery(t *testing.T) { args: args{ query: "1234/5678:BOOL[42]", }, - want: DirectPlcTag{ - PlcTag: PlcTag{ - arrayInfo: []model.ArrayInfo{ + want: model2.DirectPlcTag{ + PlcTag: model2.PlcTag{ + ArrayInfo: []model.ArrayInfo{ model3.DefaultArrayInfo{ LowerBound: 0, UpperBound: 42, @@ -121,7 +122,7 @@ func TestTagHandler_ParseQuery(t *testing.T) { IndexGroup: 1234, IndexOffset: 5678, ValueType: values.BOOL, - StringLength: NONE, + StringLength: model2.NONE, }, }, { @@ -129,9 +130,9 @@ func TestTagHandler_ParseQuery(t *testing.T) { args: args{ query: "0x04D2/0x162E:BOOL[42]", }, - want: DirectPlcTag{ - PlcTag: PlcTag{ - arrayInfo: []model.ArrayInfo{ + want: model2.DirectPlcTag{ + PlcTag: model2.PlcTag{ + ArrayInfo: []model.ArrayInfo{ model3.DefaultArrayInfo{ LowerBound: 0, UpperBound: 42, @@ -141,7 +142,7 @@ func TestTagHandler_ParseQuery(t *testing.T) { IndexGroup: 1234, IndexOffset: 5678, ValueType: values.BOOL, - StringLength: NONE, + StringLength: model2.NONE, }, }, { @@ -149,9 +150,9 @@ func TestTagHandler_ParseQuery(t *testing.T) { args: args{ query: "1234/5678:STRING(80)[42]", }, - want: DirectPlcTag{ - PlcTag: PlcTag{ - arrayInfo: []model.ArrayInfo{ + want: model2.DirectPlcTag{ + PlcTag: model2.PlcTag{ + ArrayInfo: []model.ArrayInfo{ model3.DefaultArrayInfo{ LowerBound: 0, UpperBound: 42, @@ -169,9 +170,9 @@ func TestTagHandler_ParseQuery(t *testing.T) { args: args{ query: "0x04D2/0x162E:WSTRING(80)[42]", }, - want: DirectPlcTag{ - PlcTag: PlcTag{ - arrayInfo: []model.ArrayInfo{ + want: model2.DirectPlcTag{ + PlcTag: model2.PlcTag{ + ArrayInfo: []model.ArrayInfo{ model3.DefaultArrayInfo{ LowerBound: 0, UpperBound: 42, @@ -189,9 +190,9 @@ func TestTagHandler_ParseQuery(t *testing.T) { args: args{ query: "MAIN.testVariable[42]", }, - want: SymbolicPlcTag{ - PlcTag: PlcTag{ - arrayInfo: []model.ArrayInfo{ + want: model2.SymbolicPlcTag{ + PlcTag: model2.PlcTag{ + ArrayInfo: []model.ArrayInfo{ model3.DefaultArrayInfo{ LowerBound: 0, UpperBound: 42, @@ -207,9 +208,9 @@ func TestTagHandler_ParseQuery(t *testing.T) { args: args{ query: "1234/5678:BOOL[23..42]", }, - want: DirectPlcTag{ - PlcTag: PlcTag{ - arrayInfo: []model.ArrayInfo{ + want: model2.DirectPlcTag{ + PlcTag: model2.PlcTag{ + ArrayInfo: []model.ArrayInfo{ model3.DefaultArrayInfo{ LowerBound: 23, UpperBound: 42, @@ -219,7 +220,7 @@ func TestTagHandler_ParseQuery(t *testing.T) { IndexGroup: 1234, IndexOffset: 5678, ValueType: values.BOOL, - StringLength: NONE, + StringLength: model2.NONE, }, }, { @@ -227,9 +228,9 @@ func TestTagHandler_ParseQuery(t *testing.T) { args: args{ query: "0x04D2/0x162E:BOOL[23..42]", }, - want: DirectPlcTag{ - PlcTag: PlcTag{ - arrayInfo: []model.ArrayInfo{ + want: model2.DirectPlcTag{ + PlcTag: model2.PlcTag{ + ArrayInfo: []model.ArrayInfo{ model3.DefaultArrayInfo{ LowerBound: 23, UpperBound: 42, @@ -239,7 +240,7 @@ func TestTagHandler_ParseQuery(t *testing.T) { IndexGroup: 1234, IndexOffset: 5678, ValueType: values.BOOL, - StringLength: NONE, + StringLength: model2.NONE, }, }, { @@ -247,9 +248,9 @@ func TestTagHandler_ParseQuery(t *testing.T) { args: args{ query: "1234/5678:STRING(80)[23..42]", }, - want: DirectPlcTag{ - PlcTag: PlcTag{ - arrayInfo: []model.ArrayInfo{ + want: model2.DirectPlcTag{ + PlcTag: model2.PlcTag{ + ArrayInfo: []model.ArrayInfo{ model3.DefaultArrayInfo{ LowerBound: 23, UpperBound: 42, @@ -267,9 +268,9 @@ func TestTagHandler_ParseQuery(t *testing.T) { args: args{ query: "0x04D2/0x162E:WSTRING(80)[23..42]", }, - want: DirectPlcTag{ - PlcTag: PlcTag{ - arrayInfo: []model.ArrayInfo{ + want: model2.DirectPlcTag{ + PlcTag: model2.PlcTag{ + ArrayInfo: []model.ArrayInfo{ model3.DefaultArrayInfo{ LowerBound: 23, UpperBound: 42, @@ -287,9 +288,9 @@ func TestTagHandler_ParseQuery(t *testing.T) { args: args{ query: "MAIN.testVariable[23..42]", }, - want: SymbolicPlcTag{ - PlcTag: PlcTag{ - arrayInfo: []model.ArrayInfo{ + want: model2.SymbolicPlcTag{ + PlcTag: model2.PlcTag{ + ArrayInfo: []model.ArrayInfo{ model3.DefaultArrayInfo{ LowerBound: 23, UpperBound: 42, @@ -305,9 +306,9 @@ func TestTagHandler_ParseQuery(t *testing.T) { args: args{ query: "1234/5678:BOOL[23:42]", }, - want: DirectPlcTag{ - PlcTag: PlcTag{ - arrayInfo: []model.ArrayInfo{ + want: model2.DirectPlcTag{ + PlcTag: model2.PlcTag{ + ArrayInfo: []model.ArrayInfo{ model3.DefaultArrayInfo{ LowerBound: 23, UpperBound: 65, @@ -317,7 +318,7 @@ func TestTagHandler_ParseQuery(t *testing.T) { IndexGroup: 1234, IndexOffset: 5678, ValueType: values.BOOL, - StringLength: NONE, + StringLength: model2.NONE, }, }, { @@ -325,9 +326,9 @@ func TestTagHandler_ParseQuery(t *testing.T) { args: args{ query: "0x04D2/0x162E:BOOL[23:42]", }, - want: DirectPlcTag{ - PlcTag: PlcTag{ - arrayInfo: []model.ArrayInfo{ + want: model2.DirectPlcTag{ + PlcTag: model2.PlcTag{ + ArrayInfo: []model.ArrayInfo{ model3.DefaultArrayInfo{ LowerBound: 23, UpperBound: 65, @@ -337,7 +338,7 @@ func TestTagHandler_ParseQuery(t *testing.T) { IndexGroup: 1234, IndexOffset: 5678, ValueType: values.BOOL, - StringLength: NONE, + StringLength: model2.NONE, }, }, { @@ -345,9 +346,9 @@ func TestTagHandler_ParseQuery(t *testing.T) { args: args{ query: "1234/5678:STRING(80)[23:42]", }, - want: DirectPlcTag{ - PlcTag: PlcTag{ - arrayInfo: []model.ArrayInfo{ + want: model2.DirectPlcTag{ + PlcTag: model2.PlcTag{ + ArrayInfo: []model.ArrayInfo{ model3.DefaultArrayInfo{ LowerBound: 23, UpperBound: 65, @@ -365,9 +366,9 @@ func TestTagHandler_ParseQuery(t *testing.T) { args: args{ query: "0x04D2/0x162E:WSTRING(80)[23:42]", }, - want: DirectPlcTag{ - PlcTag: PlcTag{ - arrayInfo: []model.ArrayInfo{ + want: model2.DirectPlcTag{ + PlcTag: model2.PlcTag{ + ArrayInfo: []model.ArrayInfo{ model3.DefaultArrayInfo{ LowerBound: 23, UpperBound: 65, @@ -385,9 +386,9 @@ func TestTagHandler_ParseQuery(t *testing.T) { args: args{ query: "MAIN.testVariable[23:42]", }, - want: SymbolicPlcTag{ - PlcTag: PlcTag{ - arrayInfo: []model.ArrayInfo{ + want: model2.SymbolicPlcTag{ + PlcTag: model2.PlcTag{ + ArrayInfo: []model.ArrayInfo{ model3.DefaultArrayInfo{ LowerBound: 23, UpperBound: 65, diff --git a/plc4go/internal/ads/ValueHandler.go b/plc4go/internal/ads/ValueHandler.go index 492660ad304..eacd6025d00 100644 --- a/plc4go/internal/ads/ValueHandler.go +++ b/plc4go/internal/ads/ValueHandler.go @@ -24,6 +24,7 @@ import ( "reflect" "strconv" + model2 "github.com/apache/plc4x/plc4go/internal/ads/model" "github.com/apache/plc4x/plc4go/pkg/api/model" apiValues "github.com/apache/plc4x/plc4go/pkg/api/values" driverModel "github.com/apache/plc4x/plc4go/protocols/ads/readwrite/model" @@ -55,17 +56,17 @@ func (t ValueHandler) NewPlcValue(tag model.PlcTag, value interface{}) (apiValue func (t ValueHandler) parseType(tag model.PlcTag, value interface{}) (apiValues.PlcValue, error) { // Resolve the symbolic tag to a direct tag, that has all the important information. - var directTag DirectPlcTag + var directTag model2.DirectPlcTag switch tag.(type) { - case SymbolicPlcTag: - symbolicTag := tag.(SymbolicPlcTag) + case model2.SymbolicPlcTag: + symbolicTag := tag.(model2.SymbolicPlcTag) directTagPointer, err := t.driverContext.getDirectTagForSymbolTag(symbolicTag) if err != nil { return nil, fmt.Errorf("couldn't resolve address %s to a valid tag on the PLC", symbolicTag.SymbolicAddress) } directTag = *directTagPointer - case DirectPlcTag: - directTag = tag.(DirectPlcTag) + case model2.DirectPlcTag: + directTag = tag.(model2.DirectPlcTag) } return t.AdsParseType(directTag.DataType, directTag.DataType.GetArrayInfo(), value) diff --git a/plc4go/internal/ads/Writer.go b/plc4go/internal/ads/Writer.go index 6cc8c0bc517..43470ea3c93 100644 --- a/plc4go/internal/ads/Writer.go +++ b/plc4go/internal/ads/Writer.go @@ -25,6 +25,7 @@ import ( "fmt" "strings" + "github.com/apache/plc4x/plc4go/internal/ads/model" apiModel "github.com/apache/plc4x/plc4go/pkg/api/model" "github.com/apache/plc4x/plc4go/pkg/api/values" driverModel "github.com/apache/plc4x/plc4go/protocols/ads/readwrite/model" @@ -65,8 +66,8 @@ func (m *Connection) singleWrite(ctx context.Context, writeRequest apiModel.PlcW // Here we can be sure that we're only handling a single request. tagName := writeRequest.GetTagNames()[0] tag := writeRequest.GetTag(tagName) - if needsResolving(tag) { - adsField, err := castToSymbolicPlcTagFromPlcTag(tag) + if model.NeedsResolving(tag) { + adsField, err := model.CastToSymbolicPlcTagFromPlcTag(tag) if err != nil { result <- &internalModel.DefaultPlcWriteRequestResult{ Request: writeRequest, @@ -88,7 +89,7 @@ func (m *Connection) singleWrite(ctx context.Context, writeRequest apiModel.PlcW return } } - directAdsTag, ok := tag.(*DirectPlcTag) + directAdsTag, ok := tag.(*model.DirectPlcTag) if !ok { result <- &internalModel.DefaultPlcWriteRequestResult{ Request: writeRequest, @@ -147,13 +148,13 @@ func (m *Connection) multiWrite(ctx context.Context, writeRequest apiModel.PlcWr // Calculate the size of all tags together. // Calculate the expected size of the response data. expectedResponseDataSize := uint32(0) - directAdsTags := map[string]*DirectPlcTag{} + directAdsTags := map[string]*model.DirectPlcTag{} requestItems := make([]driverModel.AdsMultiRequestItem, 0) io := utils.NewWriteBufferByteBased(utils.WithByteOrderForByteBasedBuffer(binary.LittleEndian)) for _, tagName := range writeRequest.GetTagNames() { tag := writeRequest.GetTag(tagName) - if needsResolving(tag) { - adsField, err := castToSymbolicPlcTagFromPlcTag(tag) + if model.NeedsResolving(tag) { + adsField, err := model.CastToSymbolicPlcTagFromPlcTag(tag) if err != nil { result <- &internalModel.DefaultPlcWriteRequestResult{ Request: writeRequest, @@ -175,7 +176,7 @@ func (m *Connection) multiWrite(ctx context.Context, writeRequest apiModel.PlcWr return } } - directAdsTag, ok := tag.(*DirectPlcTag) + directAdsTag, ok := tag.(*model.DirectPlcTag) if !ok { result <- &internalModel.DefaultPlcWriteRequestResult{ Request: writeRequest, diff --git a/plc4go/internal/ads/model/AdsSubscriptionHandle.go b/plc4go/internal/ads/model/AdsSubscriptionHandle.go new file mode 100644 index 00000000000..84e90e1fe53 --- /dev/null +++ b/plc4go/internal/ads/model/AdsSubscriptionHandle.go @@ -0,0 +1,72 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package model + +import ( + "time" + + apiModel "github.com/apache/plc4x/plc4go/pkg/api/model" + "github.com/apache/plc4x/plc4go/pkg/api/values" + "github.com/apache/plc4x/plc4go/spi" + "github.com/apache/plc4x/plc4go/spi/model" +) + +type AdsSubscriptionHandle struct { + subscriber spi.PlcSubscriber + tagName string + directTag DirectPlcTag + consumers []apiModel.PlcSubscriptionEventConsumer + + apiModel.PlcSubscriptionHandle +} + +func NewAdsSubscriptionHandle(subscriber spi.PlcSubscriber, tagName string, directTag DirectPlcTag) *AdsSubscriptionHandle { + return &AdsSubscriptionHandle{ + subscriber: subscriber, + tagName: tagName, + directTag: directTag, + consumers: []apiModel.PlcSubscriptionEventConsumer{}, + } +} + +func (t *AdsSubscriptionHandle) Register(consumer apiModel.PlcSubscriptionEventConsumer) apiModel.PlcConsumerRegistration { + t.consumers = append(t.consumers, consumer) + return model.NewDefaultPlcConsumerRegistration(t.subscriber, consumer, t) +} + +func (t *AdsSubscriptionHandle) GetNumConsumers() int { + return len(t.consumers) +} + +func (t *AdsSubscriptionHandle) GetDirectTag() DirectPlcTag { + return t.directTag +} + +func (t *AdsSubscriptionHandle) PublishPlcValue(value values.PlcValue) { + event := NewSubscriptionEvent( + map[string]apiModel.PlcTag{t.tagName: t.directTag}, + map[string]model.SubscriptionType{t.tagName: model.SubscriptionChangeOfState}, + map[string]time.Duration{t.tagName: time.Second}, + map[string]apiModel.PlcResponseCode{t.tagName: apiModel.PlcResponseCode_OK}, + map[string]values.PlcValue{t.tagName: value}) + for _, consumer := range t.consumers { + consumer(&event) + } +} diff --git a/plc4go/internal/ads/Configuration.go b/plc4go/internal/ads/model/Configuration.go similarity index 63% rename from plc4go/internal/ads/Configuration.go rename to plc4go/internal/ads/model/Configuration.go index 84bc0d85817..b760e5f190a 100644 --- a/plc4go/internal/ads/Configuration.go +++ b/plc4go/internal/ads/model/Configuration.go @@ -17,56 +17,57 @@ * under the License. */ -package ads +package model import ( + "strconv" + "strings" + readWriteModel "github.com/apache/plc4x/plc4go/protocols/ads/readwrite/model" "github.com/pkg/errors" "github.com/rs/zerolog/log" - "strconv" - "strings" ) type Configuration struct { - sourceAmsNetId readWriteModel.AmsNetId - sourceAmsPort uint16 - targetAmsNetId readWriteModel.AmsNetId - targetAmsPort uint16 + SourceAmsNetId readWriteModel.AmsNetId + SourceAmsPort uint16 + TargetAmsNetId readWriteModel.AmsNetId + TargetAmsPort uint16 } func ParseFromOptions(options map[string][]string) (Configuration, error) { configuration := Configuration{} - sourceAmsNetId := getFromOptions(options, "sourceAmsNetId") + sourceAmsNetId := getFromOptions(options, "SourceAmsNetId") if sourceAmsNetId == "" { - return Configuration{}, errors.New("Required parameter sourceAmsNetId missing") + return Configuration{}, errors.New("Required parameter SourceAmsNetId missing") } split := strings.Split(sourceAmsNetId, ".") octet1, err := strconv.ParseUint(split[0], 10, 8) if err != nil { - return Configuration{}, errors.Wrap(err, "error parsing sourceAmsNetId") + return Configuration{}, errors.Wrap(err, "error parsing SourceAmsNetId") } octet2, err := strconv.ParseUint(split[1], 10, 8) if err != nil { - return Configuration{}, errors.Wrap(err, "error parsing sourceAmsNetId") + return Configuration{}, errors.Wrap(err, "error parsing SourceAmsNetId") } octet3, err := strconv.ParseUint(split[2], 10, 8) if err != nil { - return Configuration{}, errors.Wrap(err, "error parsing sourceAmsNetId") + return Configuration{}, errors.Wrap(err, "error parsing SourceAmsNetId") } octet4, err := strconv.ParseUint(split[3], 10, 8) if err != nil { - return Configuration{}, errors.Wrap(err, "error parsing sourceAmsNetId") + return Configuration{}, errors.Wrap(err, "error parsing SourceAmsNetId") } octet5, err := strconv.ParseUint(split[4], 10, 8) if err != nil { - return Configuration{}, errors.Wrap(err, "error parsing sourceAmsNetId") + return Configuration{}, errors.Wrap(err, "error parsing SourceAmsNetId") } octet6, err := strconv.ParseUint(split[5], 10, 8) if err != nil { - return Configuration{}, errors.Wrap(err, "error parsing sourceAmsNetId") + return Configuration{}, errors.Wrap(err, "error parsing SourceAmsNetId") } - configuration.sourceAmsNetId = readWriteModel.NewAmsNetId( + configuration.SourceAmsNetId = readWriteModel.NewAmsNetId( uint8(octet1), uint8(octet2), uint8(octet3), @@ -74,45 +75,45 @@ func ParseFromOptions(options map[string][]string) (Configuration, error) { uint8(octet5), uint8(octet6), ) - sourceAmsPort := getFromOptions(options, "sourceAmsPort") + sourceAmsPort := getFromOptions(options, "SourceAmsPort") if sourceAmsPort == "" { - return Configuration{}, errors.New("Required parameter sourceAmsPort missing") + return Configuration{}, errors.New("Required parameter SourceAmsPort missing") } parsedUint, err := strconv.ParseUint(sourceAmsPort, 10, 16) if err != nil { - return Configuration{}, errors.Wrap(err, "error parsing sourceAmsPort") + return Configuration{}, errors.Wrap(err, "error parsing SourceAmsPort") } - configuration.sourceAmsPort = uint16(parsedUint) - targetAmsNetId := getFromOptions(options, "targetAmsNetId") + configuration.SourceAmsPort = uint16(parsedUint) + targetAmsNetId := getFromOptions(options, "TargetAmsNetId") if sourceAmsNetId == "" { - return Configuration{}, errors.New("Required parameter targetAmsNetId missing") + return Configuration{}, errors.New("Required parameter TargetAmsNetId missing") } split = strings.Split(targetAmsNetId, ".") octet1, err = strconv.ParseUint(split[0], 10, 8) if err != nil { - return Configuration{}, errors.Wrap(err, "error parsing targetAmsNetId") + return Configuration{}, errors.Wrap(err, "error parsing TargetAmsNetId") } octet2, err = strconv.ParseUint(split[1], 10, 8) if err != nil { - return Configuration{}, errors.Wrap(err, "error parsing targetAmsNetId") + return Configuration{}, errors.Wrap(err, "error parsing TargetAmsNetId") } octet3, err = strconv.ParseUint(split[2], 10, 8) if err != nil { - return Configuration{}, errors.Wrap(err, "error parsing targetAmsNetId") + return Configuration{}, errors.Wrap(err, "error parsing TargetAmsNetId") } octet4, err = strconv.ParseUint(split[3], 10, 8) if err != nil { - return Configuration{}, errors.Wrap(err, "error parsing targetAmsNetId") + return Configuration{}, errors.Wrap(err, "error parsing TargetAmsNetId") } octet5, err = strconv.ParseUint(split[4], 10, 8) if err != nil { - return Configuration{}, errors.Wrap(err, "error parsing targetAmsNetId") + return Configuration{}, errors.Wrap(err, "error parsing TargetAmsNetId") } octet6, err = strconv.ParseUint(split[5], 10, 8) if err != nil { - return Configuration{}, errors.Wrap(err, "error parsing targetAmsNetId") + return Configuration{}, errors.Wrap(err, "error parsing TargetAmsNetId") } - configuration.targetAmsNetId = readWriteModel.NewAmsNetId( + configuration.TargetAmsNetId = readWriteModel.NewAmsNetId( uint8(octet1), uint8(octet2), uint8(octet3), @@ -120,15 +121,15 @@ func ParseFromOptions(options map[string][]string) (Configuration, error) { uint8(octet5), uint8(octet6), ) - targetAmsPort := getFromOptions(options, "targetAmsPort") + targetAmsPort := getFromOptions(options, "TargetAmsPort") if targetAmsPort == "" { - return Configuration{}, errors.New("Required parameter targetAmsPort missing") + return Configuration{}, errors.New("Required parameter TargetAmsPort missing") } parsedUint, err = strconv.ParseUint(targetAmsPort, 10, 16) if err != nil { - return Configuration{}, errors.Wrap(err, "error parsing targetAmsPort") + return Configuration{}, errors.Wrap(err, "error parsing TargetAmsPort") } - configuration.targetAmsPort = uint16(parsedUint) + configuration.TargetAmsPort = uint16(parsedUint) return configuration, nil } diff --git a/plc4go/internal/ads/model/SubscriptionEvent.go b/plc4go/internal/ads/model/SubscriptionEvent.go new file mode 100644 index 00000000000..2c4224bee14 --- /dev/null +++ b/plc4go/internal/ads/model/SubscriptionEvent.go @@ -0,0 +1,38 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package model + +import ( + "time" + + apiModel "github.com/apache/plc4x/plc4go/pkg/api/model" + "github.com/apache/plc4x/plc4go/pkg/api/values" + spiModel "github.com/apache/plc4x/plc4go/spi/model" +) + +type SubscriptionEvent struct { + spiModel.DefaultPlcSubscriptionEvent +} + +func NewSubscriptionEvent(tags map[string]apiModel.PlcTag, types map[string]spiModel.SubscriptionType, intervals map[string]time.Duration, responseCodes map[string]apiModel.PlcResponseCode, values map[string]values.PlcValue) SubscriptionEvent { + subscriptionEvent := SubscriptionEvent{} + subscriptionEvent.DefaultPlcSubscriptionEvent = spiModel.NewDefaultPlcSubscriptionEvent(&subscriptionEvent, tags, types, intervals, responseCodes, values) + return subscriptionEvent +} diff --git a/plc4go/internal/ads/Tag.go b/plc4go/internal/ads/model/Tag.go similarity index 91% rename from plc4go/internal/ads/Tag.go rename to plc4go/internal/ads/model/Tag.go index 717ec94f4af..6f77f701c0c 100644 --- a/plc4go/internal/ads/Tag.go +++ b/plc4go/internal/ads/model/Tag.go @@ -17,7 +17,7 @@ * under the License. */ -package ads +package model import ( "encoding/binary" @@ -37,10 +37,10 @@ const NONE = int32(-1) type PlcTag struct { model.PlcTag - arrayInfo []model.ArrayInfo + ArrayInfo []model.ArrayInfo } -func needsResolving(plcTag model.PlcTag) bool { +func NeedsResolving(plcTag model.PlcTag) bool { switch plcTag.(type) { case SymbolicPlcTag: return true @@ -61,19 +61,19 @@ type DirectPlcTag struct { DataType driverModel.AdsDataTypeTableEntry } -func newDirectAdsPlcTag(indexGroup uint32, indexOffset uint32, valueType values.PlcValueType, stringLength int32, arrayInfo []model.ArrayInfo) (model.PlcTag, error) { +func NewDirectAdsPlcTag(indexGroup uint32, indexOffset uint32, valueType values.PlcValueType, stringLength int32, arrayInfo []model.ArrayInfo) (model.PlcTag, error) { return DirectPlcTag{ IndexGroup: indexGroup, IndexOffset: indexOffset, ValueType: valueType, StringLength: stringLength, PlcTag: PlcTag{ - arrayInfo: arrayInfo, + ArrayInfo: arrayInfo, }, }, nil } -func castToDirectAdsTagFromPlcTag(plcTag model.PlcTag) (DirectPlcTag, error) { +func CastToDirectAdsTagFromPlcTag(plcTag model.PlcTag) (DirectPlcTag, error) { if adsTag, ok := plcTag.(DirectPlcTag); ok { return adsTag, nil } @@ -85,8 +85,8 @@ func (m DirectPlcTag) GetAddressString() string { if m.ValueType == values.STRING || m.ValueType == values.WSTRING { address = address + "(" + strconv.Itoa(int(m.StringLength)) + ")" } - if len(m.arrayInfo) > 0 { - for _, ai := range m.arrayInfo { + if len(m.ArrayInfo) > 0 { + for _, ai := range m.ArrayInfo { address = address + "[" + strconv.Itoa(int(ai.GetLowerBound())) + ".." + strconv.Itoa(int(ai.GetUpperBound())) + "]" } } @@ -128,11 +128,11 @@ func (m DirectPlcTag) SerializeWithWriteBuffer(writeBuffer utils.WriteBuffer) er return err } } - if len(m.arrayInfo) > 0 { + if len(m.ArrayInfo) > 0 { if err := writeBuffer.PushContext("ArrayInfo"); err != nil { return err } - for _, ai := range m.arrayInfo { + for _, ai := range m.ArrayInfo { if err := writeBuffer.PushContext("ArrayInfo"); err != nil { return err } @@ -167,16 +167,16 @@ type SymbolicPlcTag struct { SymbolicAddress string } -func newAdsSymbolicPlcTag(symbolicAddress string, arrayInfo []model.ArrayInfo) (model.PlcTag, error) { +func NewAdsSymbolicPlcTag(symbolicAddress string, arrayInfo []model.ArrayInfo) (model.PlcTag, error) { return SymbolicPlcTag{ SymbolicAddress: symbolicAddress, PlcTag: PlcTag{ - arrayInfo: arrayInfo, + ArrayInfo: arrayInfo, }, }, nil } -func castToSymbolicPlcTagFromPlcTag(plcTag model.PlcTag) (SymbolicPlcTag, error) { +func CastToSymbolicPlcTagFromPlcTag(plcTag model.PlcTag) (SymbolicPlcTag, error) { if adsTag, ok := plcTag.(SymbolicPlcTag); ok { return adsTag, nil } @@ -211,11 +211,11 @@ func (m SymbolicPlcTag) SerializeWithWriteBuffer(writeBuffer utils.WriteBuffer) if err := writeBuffer.WriteString("symbolicAddress", uint32(len([]rune(m.SymbolicAddress))*8), "UTF-8", m.SymbolicAddress); err != nil { return err } - if len(m.arrayInfo) > 0 { + if len(m.ArrayInfo) > 0 { if err := writeBuffer.PushContext("ArrayInfo"); err != nil { return err } - for _, ai := range m.arrayInfo { + for _, ai := range m.ArrayInfo { if err := writeBuffer.PushContext("ArrayInfo"); err != nil { return err } diff --git a/plc4go/internal/knxnetip/SubscriptionEvent.go b/plc4go/internal/knxnetip/SubscriptionEvent.go index 76c7407d260..940367e42c7 100644 --- a/plc4go/internal/knxnetip/SubscriptionEvent.go +++ b/plc4go/internal/knxnetip/SubscriptionEvent.go @@ -25,19 +25,19 @@ import ( apiModel "github.com/apache/plc4x/plc4go/pkg/api/model" "github.com/apache/plc4x/plc4go/pkg/api/values" driverModel "github.com/apache/plc4x/plc4go/protocols/knxnetip/readwrite/model" - internalMode "github.com/apache/plc4x/plc4go/spi/model" + internalModel "github.com/apache/plc4x/plc4go/spi/model" ) type SubscriptionEvent struct { - internalMode.DefaultPlcSubscriptionEvent + internalModel.DefaultPlcSubscriptionEvent addresses map[string][]byte } -func NewSubscriptionEvent(tags map[string]apiModel.PlcTag, types map[string]internalMode.SubscriptionType, +func NewSubscriptionEvent(tags map[string]apiModel.PlcTag, types map[string]internalModel.SubscriptionType, intervals map[string]time.Duration, responseCodes map[string]apiModel.PlcResponseCode, addresses map[string][]byte, values map[string]values.PlcValue) SubscriptionEvent { subscriptionEvent := SubscriptionEvent{addresses: addresses} - subscriptionEvent.DefaultPlcSubscriptionEvent = internalMode.NewDefaultPlcSubscriptionEvent(&subscriptionEvent, tags, types, intervals, responseCodes, values) + subscriptionEvent.DefaultPlcSubscriptionEvent = internalModel.NewDefaultPlcSubscriptionEvent(&subscriptionEvent, tags, types, intervals, responseCodes, values) return subscriptionEvent } diff --git a/plc4go/pkg/api/model/plc_subscription.go b/plc4go/pkg/api/model/plc_subscription.go index 2fe6be93bf1..10f32de1c5d 100644 --- a/plc4go/pkg/api/model/plc_subscription.go +++ b/plc4go/pkg/api/model/plc_subscription.go @@ -66,6 +66,9 @@ type PlcSubscriptionRequest interface { PlcRequest Execute() <-chan PlcSubscriptionRequestResult ExecuteWithContext(ctx context.Context) <-chan PlcSubscriptionRequestResult + + GetTagNames() []string + GetTag(tagName string) PlcTag } type PlcSubscriptionResponse interface { diff --git a/plc4go/protocols/knxnetip/readwrite/model/KnxManufacturer.go b/plc4go/protocols/knxnetip/readwrite/model/KnxManufacturer.go index db34e8e9302..b5c6147d625 100644 --- a/plc4go/protocols/knxnetip/readwrite/model/KnxManufacturer.go +++ b/plc4go/protocols/knxnetip/readwrite/model/KnxManufacturer.go @@ -643,8 +643,9 @@ const ( KnxManufacturer_M_STUHL_REGELSYSTEME_GMBH KnxManufacturer = 604 KnxManufacturer_M_SHENZHEN_GLUCK_TECHNOLOGY_CO___LTD KnxManufacturer = 605 KnxManufacturer_M_GAIMEX KnxManufacturer = 606 - KnxManufacturer_M_ABB___RESERVED KnxManufacturer = 607 - KnxManufacturer_M_BUSCH_JAEGER_ELEKTRO___RESERVED KnxManufacturer = 608 + KnxManufacturer_M_B3_INTERNATIONAL_S_R_L KnxManufacturer = 607 + KnxManufacturer_M_ABB___RESERVED KnxManufacturer = 608 + KnxManufacturer_M_BUSCH_JAEGER_ELEKTRO___RESERVED KnxManufacturer = 609 ) var KnxManufacturerValues []KnxManufacturer @@ -1259,6 +1260,7 @@ func init() { KnxManufacturer_M_STUHL_REGELSYSTEME_GMBH, KnxManufacturer_M_SHENZHEN_GLUCK_TECHNOLOGY_CO___LTD, KnxManufacturer_M_GAIMEX, + KnxManufacturer_M_B3_INTERNATIONAL_S_R_L, KnxManufacturer_M_ABB___RESERVED, KnxManufacturer_M_BUSCH_JAEGER_ELEKTRO___RESERVED, } @@ -3528,10 +3530,14 @@ func (e KnxManufacturer) Number() uint16 { } case 607: { /* '607' */ - return 43954 + return 665 } case 608: { /* '608' */ + return 43954 + } + case 609: + { /* '609' */ return 43959 } case 61: @@ -5982,10 +5988,14 @@ func (e KnxManufacturer) Name() string { } case 607: { /* '607' */ - return "ABB - reserved" + return "B3 International S.R.L" } case 608: { /* '608' */ + return "ABB - reserved" + } + case 609: + { /* '609' */ return "Busch-Jaeger Elektro - reserved" } case 61: @@ -7304,8 +7314,10 @@ func KnxManufacturerByValue(value uint16) (enum KnxManufacturer, ok bool) { case 606: return KnxManufacturer_M_GAIMEX, true case 607: - return KnxManufacturer_M_ABB___RESERVED, true + return KnxManufacturer_M_B3_INTERNATIONAL_S_R_L, true case 608: + return KnxManufacturer_M_ABB___RESERVED, true + case 609: return KnxManufacturer_M_BUSCH_JAEGER_ELEKTRO___RESERVED, true case 61: return KnxManufacturer_M_SCHNEIDER_ELECTRIC_INDUSTRIES_SAS, true @@ -8527,6 +8539,8 @@ func KnxManufacturerByName(value string) (enum KnxManufacturer, ok bool) { return KnxManufacturer_M_SHENZHEN_GLUCK_TECHNOLOGY_CO___LTD, true case "M_GAIMEX": return KnxManufacturer_M_GAIMEX, true + case "M_B3_INTERNATIONAL_S_R_L": + return KnxManufacturer_M_B3_INTERNATIONAL_S_R_L, true case "M_ABB___RESERVED": return KnxManufacturer_M_ABB___RESERVED, true case "M_BUSCH_JAEGER_ELEKTRO___RESERVED": @@ -9808,6 +9822,8 @@ func (e KnxManufacturer) PLC4XEnumName() string { return "M_SHENZHEN_GLUCK_TECHNOLOGY_CO___LTD" case KnxManufacturer_M_GAIMEX: return "M_GAIMEX" + case KnxManufacturer_M_B3_INTERNATIONAL_S_R_L: + return "M_B3_INTERNATIONAL_S_R_L" case KnxManufacturer_M_ABB___RESERVED: return "M_ABB___RESERVED" case KnxManufacturer_M_BUSCH_JAEGER_ELEKTRO___RESERVED: diff --git a/plc4go/spi/model/DefaultPlcSubscriptionRequest.go b/plc4go/spi/model/DefaultPlcSubscriptionRequest.go index 2e5eb6a3fa8..ffb55934444 100644 --- a/plc4go/spi/model/DefaultPlcSubscriptionRequest.go +++ b/plc4go/spi/model/DefaultPlcSubscriptionRequest.go @@ -138,7 +138,7 @@ func (d *DefaultPlcSubscriptionRequestBuilder) Build() (model.PlcSubscriptionReq d.tags[name] = tag } } - return NewDefaultPlcSubscriptionRequest(d.tags, d.tagNames, d.types, d.intervals, d.subscriber, d.preRegisteredConsumers), nil + return NewDefaultPlcSubscriptionRequest(d.subscriber, d.tagNames, d.tags, d.types, d.intervals, d.preRegisteredConsumers), nil } //go:generate go run ../../tools/plc4xgenerator/gen.go -type=DefaultPlcSubscriptionRequest @@ -146,12 +146,12 @@ type DefaultPlcSubscriptionRequest struct { DefaultPlcTagRequest types map[string]SubscriptionType intervals map[string]time.Duration - subscriber spi.PlcSubscriber preRegisteredConsumers map[string][]model.PlcSubscriptionEventConsumer `ignore:"true"` + subscriber spi.PlcSubscriber } -func NewDefaultPlcSubscriptionRequest(tags map[string]model.PlcTag, tagNames []string, types map[string]SubscriptionType, intervals map[string]time.Duration, subscriber spi.PlcSubscriber, preRegisteredConsumers map[string][]model.PlcSubscriptionEventConsumer) model.PlcSubscriptionRequest { - return &DefaultPlcSubscriptionRequest{NewDefaultPlcTagRequest(tags, tagNames), types, intervals, subscriber, preRegisteredConsumers} +func NewDefaultPlcSubscriptionRequest(subscriber spi.PlcSubscriber, tagNames []string, tags map[string]model.PlcTag, types map[string]SubscriptionType, intervals map[string]time.Duration, preRegisteredConsumers map[string][]model.PlcSubscriptionEventConsumer) model.PlcSubscriptionRequest { + return &DefaultPlcSubscriptionRequest{NewDefaultPlcTagRequest(tags, tagNames), types, intervals, preRegisteredConsumers, subscriber} } func (d *DefaultPlcSubscriptionRequest) Execute() <-chan model.PlcSubscriptionRequestResult { diff --git a/plc4j/drivers/knxnetip/src/main/generated/org/apache/plc4x/java/knxnetip/readwrite/KnxManufacturer.java b/plc4j/drivers/knxnetip/src/main/generated/org/apache/plc4x/java/knxnetip/readwrite/KnxManufacturer.java index b6569d18430..8a5a2fd65a1 100644 --- a/plc4j/drivers/knxnetip/src/main/generated/org/apache/plc4x/java/knxnetip/readwrite/KnxManufacturer.java +++ b/plc4j/drivers/knxnetip/src/main/generated/org/apache/plc4x/java/knxnetip/readwrite/KnxManufacturer.java @@ -697,9 +697,10 @@ public enum KnxManufacturer { M_SHENZHEN_GLUCK_TECHNOLOGY_CO___LTD( (int) 605, (int) 663, (String) "Shenzhen Gluck Technology Co., LTD"), M_GAIMEX((int) 606, (int) 664, (String) "Gaimex"), - M_ABB___RESERVED((int) 607, (int) 43954, (String) "ABB - reserved"), + M_B3_INTERNATIONAL_S_R_L((int) 607, (int) 665, (String) "B3 International S.R.L"), + M_ABB___RESERVED((int) 608, (int) 43954, (String) "ABB - reserved"), M_BUSCH_JAEGER_ELEKTRO___RESERVED( - (int) 608, (int) 43959, (String) "Busch-Jaeger Elektro - reserved"); + (int) 609, (int) 43959, (String) "Busch-Jaeger Elektro - reserved"); private static final Map map; static { diff --git a/plc4net/drivers/knxnetip/src/drivers/knxnetip/readwrite/model/KnxManufacturer.cs b/plc4net/drivers/knxnetip/src/drivers/knxnetip/readwrite/model/KnxManufacturer.cs index dd8041c34f5..890dfb2b242 100644 --- a/plc4net/drivers/knxnetip/src/drivers/knxnetip/readwrite/model/KnxManufacturer.cs +++ b/plc4net/drivers/knxnetip/src/drivers/knxnetip/readwrite/model/KnxManufacturer.cs @@ -631,8 +631,9 @@ public enum KnxManufacturer M_STUHL_REGELSYSTEME_GMBH = 604, M_SHENZHEN_GLUCK_TECHNOLOGY_CO___LTD = 605, M_GAIMEX = 606, - M_ABB___RESERVED = 607, - M_BUSCH_JAEGER_ELEKTRO___RESERVED = 608, + M_B3_INTERNATIONAL_S_R_L = 607, + M_ABB___RESERVED = 608, + M_BUSCH_JAEGER_ELEKTRO___RESERVED = 609, } public static class KnxManufacturerInfo @@ -2337,10 +2338,13 @@ public static class KnxManufacturerInfo case KnxManufacturer.M_GAIMEX: { /* '606' */ return 664; } - case KnxManufacturer.M_ABB___RESERVED: { /* '607' */ + case KnxManufacturer.M_B3_INTERNATIONAL_S_R_L: { /* '607' */ + return 665; + } + case KnxManufacturer.M_ABB___RESERVED: { /* '608' */ return 43954; } - case KnxManufacturer.M_BUSCH_JAEGER_ELEKTRO___RESERVED: { /* '608' */ + case KnxManufacturer.M_BUSCH_JAEGER_ELEKTRO___RESERVED: { /* '609' */ return 43959; } case KnxManufacturer.M_SCHNEIDER_ELECTRIC_INDUSTRIES_SAS: { /* '61' */ @@ -4174,10 +4178,13 @@ public static string Name(this KnxManufacturer value) case KnxManufacturer.M_GAIMEX: { /* '606' */ return "Gaimex"; } - case KnxManufacturer.M_ABB___RESERVED: { /* '607' */ + case KnxManufacturer.M_B3_INTERNATIONAL_S_R_L: { /* '607' */ + return "B3 International S.R.L"; + } + case KnxManufacturer.M_ABB___RESERVED: { /* '608' */ return "ABB - reserved"; } - case KnxManufacturer.M_BUSCH_JAEGER_ELEKTRO___RESERVED: { /* '608' */ + case KnxManufacturer.M_BUSCH_JAEGER_ELEKTRO___RESERVED: { /* '609' */ return "Busch-Jaeger Elektro - reserved"; } case KnxManufacturer.M_SCHNEIDER_ELECTRIC_INDUSTRIES_SAS: { /* '61' */