Add bulk MXAccess subscription commands
This commit is contained in:
@@ -137,6 +137,44 @@ public sealed class MxGatewayClientSessionTests
|
||||
Assert.Equal(56, request.Command.Write2.UserId);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task SubscribeBulkAsync_BuildsOneBulkCommandAndReturnsPerItemResults()
|
||||
{
|
||||
FakeGatewayTransport transport = CreateTransport();
|
||||
transport.AddInvokeReply(new MxCommandReply
|
||||
{
|
||||
SessionId = "session-fixture",
|
||||
Kind = MxCommandKind.SubscribeBulk,
|
||||
ProtocolStatus = new ProtocolStatus { Code = ProtocolStatusCode.Ok },
|
||||
SubscribeBulk = new BulkSubscribeReply
|
||||
{
|
||||
Results =
|
||||
{
|
||||
new SubscribeResult
|
||||
{
|
||||
ServerHandle = 12,
|
||||
TagAddress = "Area001.Pump001.Speed",
|
||||
ItemHandle = 34,
|
||||
WasSuccessful = true,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
await using MxGatewayClient client = CreateClient(transport);
|
||||
MxGatewaySession session = await client.OpenSessionAsync();
|
||||
|
||||
IReadOnlyList<SubscribeResult> results = await session.SubscribeBulkAsync(
|
||||
12,
|
||||
["Area001.Pump001.Speed"]);
|
||||
|
||||
SubscribeResult result = Assert.Single(results);
|
||||
Assert.Equal(34, result.ItemHandle);
|
||||
MxCommandRequest request = Assert.Single(transport.InvokeCalls).Request;
|
||||
Assert.Equal(MxCommandKind.SubscribeBulk, request.Command.Kind);
|
||||
Assert.Equal(12, request.Command.SubscribeBulk.ServerHandle);
|
||||
Assert.Equal(["Area001.Pump001.Speed"], request.Command.SubscribeBulk.TagAddresses);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task StreamEventsAsync_YieldsEventsInGatewayOrder()
|
||||
{
|
||||
|
||||
@@ -175,6 +175,194 @@ public sealed class MxGatewaySession : IAsyncDisposable
|
||||
cancellationToken);
|
||||
}
|
||||
|
||||
public async Task UnAdviseAsync(
|
||||
int serverHandle,
|
||||
int itemHandle,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
MxCommandReply reply = await UnAdviseRawAsync(serverHandle, itemHandle, cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
reply.EnsureProtocolSuccess().EnsureMxAccessSuccess();
|
||||
}
|
||||
|
||||
public Task<MxCommandReply> UnAdviseRawAsync(
|
||||
int serverHandle,
|
||||
int itemHandle,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
return InvokeCommandAsync(
|
||||
new MxCommand
|
||||
{
|
||||
Kind = MxCommandKind.UnAdvise,
|
||||
UnAdvise = new UnAdviseCommand
|
||||
{
|
||||
ServerHandle = serverHandle,
|
||||
ItemHandle = itemHandle,
|
||||
},
|
||||
},
|
||||
cancellationToken);
|
||||
}
|
||||
|
||||
public async Task RemoveItemAsync(
|
||||
int serverHandle,
|
||||
int itemHandle,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
MxCommandReply reply = await RemoveItemRawAsync(serverHandle, itemHandle, cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
reply.EnsureProtocolSuccess().EnsureMxAccessSuccess();
|
||||
}
|
||||
|
||||
public Task<MxCommandReply> RemoveItemRawAsync(
|
||||
int serverHandle,
|
||||
int itemHandle,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
return InvokeCommandAsync(
|
||||
new MxCommand
|
||||
{
|
||||
Kind = MxCommandKind.RemoveItem,
|
||||
RemoveItem = new RemoveItemCommand
|
||||
{
|
||||
ServerHandle = serverHandle,
|
||||
ItemHandle = itemHandle,
|
||||
},
|
||||
},
|
||||
cancellationToken);
|
||||
}
|
||||
|
||||
public async Task<IReadOnlyList<SubscribeResult>> AddItemBulkAsync(
|
||||
int serverHandle,
|
||||
IReadOnlyList<string> tagAddresses,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(tagAddresses);
|
||||
|
||||
AddItemBulkCommand command = new() { ServerHandle = serverHandle };
|
||||
command.TagAddresses.Add(tagAddresses);
|
||||
|
||||
MxCommandReply reply = await InvokeCommandAsync(
|
||||
new MxCommand
|
||||
{
|
||||
Kind = MxCommandKind.AddItemBulk,
|
||||
AddItemBulk = command,
|
||||
},
|
||||
cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
reply.EnsureProtocolSuccess().EnsureMxAccessSuccess();
|
||||
return reply.AddItemBulk?.Results.ToArray() ?? [];
|
||||
}
|
||||
|
||||
public async Task<IReadOnlyList<SubscribeResult>> AdviseItemBulkAsync(
|
||||
int serverHandle,
|
||||
IReadOnlyList<int> itemHandles,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(itemHandles);
|
||||
|
||||
AdviseItemBulkCommand command = new() { ServerHandle = serverHandle };
|
||||
command.ItemHandles.Add(itemHandles);
|
||||
|
||||
MxCommandReply reply = await InvokeCommandAsync(
|
||||
new MxCommand
|
||||
{
|
||||
Kind = MxCommandKind.AdviseItemBulk,
|
||||
AdviseItemBulk = command,
|
||||
},
|
||||
cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
reply.EnsureProtocolSuccess().EnsureMxAccessSuccess();
|
||||
return reply.AdviseItemBulk?.Results.ToArray() ?? [];
|
||||
}
|
||||
|
||||
public async Task<IReadOnlyList<SubscribeResult>> RemoveItemBulkAsync(
|
||||
int serverHandle,
|
||||
IReadOnlyList<int> itemHandles,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(itemHandles);
|
||||
|
||||
RemoveItemBulkCommand command = new() { ServerHandle = serverHandle };
|
||||
command.ItemHandles.Add(itemHandles);
|
||||
|
||||
MxCommandReply reply = await InvokeCommandAsync(
|
||||
new MxCommand
|
||||
{
|
||||
Kind = MxCommandKind.RemoveItemBulk,
|
||||
RemoveItemBulk = command,
|
||||
},
|
||||
cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
reply.EnsureProtocolSuccess().EnsureMxAccessSuccess();
|
||||
return reply.RemoveItemBulk?.Results.ToArray() ?? [];
|
||||
}
|
||||
|
||||
public async Task<IReadOnlyList<SubscribeResult>> UnAdviseItemBulkAsync(
|
||||
int serverHandle,
|
||||
IReadOnlyList<int> itemHandles,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(itemHandles);
|
||||
|
||||
UnAdviseItemBulkCommand command = new() { ServerHandle = serverHandle };
|
||||
command.ItemHandles.Add(itemHandles);
|
||||
|
||||
MxCommandReply reply = await InvokeCommandAsync(
|
||||
new MxCommand
|
||||
{
|
||||
Kind = MxCommandKind.UnAdviseItemBulk,
|
||||
UnAdviseItemBulk = command,
|
||||
},
|
||||
cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
reply.EnsureProtocolSuccess().EnsureMxAccessSuccess();
|
||||
return reply.UnAdviseItemBulk?.Results.ToArray() ?? [];
|
||||
}
|
||||
|
||||
public async Task<IReadOnlyList<SubscribeResult>> SubscribeBulkAsync(
|
||||
int serverHandle,
|
||||
IReadOnlyList<string> tagAddresses,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(tagAddresses);
|
||||
|
||||
SubscribeBulkCommand command = new() { ServerHandle = serverHandle };
|
||||
command.TagAddresses.Add(tagAddresses);
|
||||
|
||||
MxCommandReply reply = await InvokeCommandAsync(
|
||||
new MxCommand
|
||||
{
|
||||
Kind = MxCommandKind.SubscribeBulk,
|
||||
SubscribeBulk = command,
|
||||
},
|
||||
cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
reply.EnsureProtocolSuccess().EnsureMxAccessSuccess();
|
||||
return reply.SubscribeBulk?.Results.ToArray() ?? [];
|
||||
}
|
||||
|
||||
public async Task<IReadOnlyList<SubscribeResult>> UnsubscribeBulkAsync(
|
||||
int serverHandle,
|
||||
IReadOnlyList<int> itemHandles,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(itemHandles);
|
||||
|
||||
UnsubscribeBulkCommand command = new() { ServerHandle = serverHandle };
|
||||
command.ItemHandles.Add(itemHandles);
|
||||
|
||||
MxCommandReply reply = await InvokeCommandAsync(
|
||||
new MxCommand
|
||||
{
|
||||
Kind = MxCommandKind.UnsubscribeBulk,
|
||||
UnsubscribeBulk = command,
|
||||
},
|
||||
cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
reply.EnsureProtocolSuccess().EnsureMxAccessSuccess();
|
||||
return reply.UnsubscribeBulk?.Results.ToArray() ?? [];
|
||||
}
|
||||
|
||||
public async Task WriteAsync(
|
||||
int serverHandle,
|
||||
int itemHandle,
|
||||
@@ -297,4 +485,5 @@ public sealed class MxGatewaySession : IAsyncDisposable
|
||||
},
|
||||
cancellationToken);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -117,6 +117,49 @@ func TestSessionHelpersBuildCommandsAndExposeRawReply(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestSubscribeBulkBuildsOneBulkCommandAndReturnsResults(t *testing.T) {
|
||||
fake := &fakeGatewayServer{
|
||||
invokeReply: &pb.MxCommandReply{
|
||||
SessionId: "session-1",
|
||||
Kind: pb.MxCommandKind_MX_COMMAND_KIND_SUBSCRIBE_BULK,
|
||||
ProtocolStatus: &pb.ProtocolStatus{
|
||||
Code: pb.ProtocolStatusCode_PROTOCOL_STATUS_CODE_OK,
|
||||
},
|
||||
Payload: &pb.MxCommandReply_SubscribeBulk{
|
||||
SubscribeBulk: &pb.BulkSubscribeReply{
|
||||
Results: []*pb.SubscribeResult{
|
||||
{
|
||||
ServerHandle: 12,
|
||||
TagAddress: "Area001.Pump001.Speed",
|
||||
ItemHandle: 34,
|
||||
WasSuccessful: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
client, cleanup := newBufconnClient(t, fake)
|
||||
defer cleanup()
|
||||
session := NewSessionForID(client, "session-1")
|
||||
|
||||
results, err := session.SubscribeBulk(context.Background(), 12, []string{"Area001.Pump001.Speed"})
|
||||
if err != nil {
|
||||
t.Fatalf("SubscribeBulk() error = %v", err)
|
||||
}
|
||||
|
||||
if len(results) != 1 || results[0].GetItemHandle() != 34 {
|
||||
t.Fatalf("results = %#v, want item handle 34", results)
|
||||
}
|
||||
req := fake.invokeRequest
|
||||
if req.GetCommand().GetKind() != pb.MxCommandKind_MX_COMMAND_KIND_SUBSCRIBE_BULK {
|
||||
t.Fatalf("command kind = %s", req.GetCommand().GetKind())
|
||||
}
|
||||
if got := req.GetCommand().GetSubscribeBulk().GetTagAddresses(); len(got) != 1 || got[0] != "Area001.Pump001.Speed" {
|
||||
t.Fatalf("tag addresses = %#v", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestInvokeReturnsTypedMxAccessErrorWithRawReply(t *testing.T) {
|
||||
hresult := int32(-2147467259)
|
||||
fake := &fakeGatewayServer{
|
||||
|
||||
@@ -104,6 +104,25 @@ func (s *Session) Unregister(ctx context.Context, serverHandle int32) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// RemoveItem invokes MXAccess RemoveItem.
|
||||
func (s *Session) RemoveItem(ctx context.Context, serverHandle, itemHandle int32) error {
|
||||
_, err := s.RemoveItemRaw(ctx, serverHandle, itemHandle)
|
||||
return err
|
||||
}
|
||||
|
||||
// RemoveItemRaw invokes MXAccess RemoveItem and returns the raw reply.
|
||||
func (s *Session) RemoveItemRaw(ctx context.Context, serverHandle, itemHandle int32) (*MxCommandReply, error) {
|
||||
return s.invokeCommand(ctx, &pb.MxCommand{
|
||||
Kind: pb.MxCommandKind_MX_COMMAND_KIND_REMOVE_ITEM,
|
||||
Payload: &pb.MxCommand_RemoveItem{
|
||||
RemoveItem: &pb.RemoveItemCommand{
|
||||
ServerHandle: serverHandle,
|
||||
ItemHandle: itemHandle,
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// AddItem invokes MXAccess AddItem and returns the item handle.
|
||||
func (s *Session) AddItem(ctx context.Context, serverHandle int32, itemDefinition string) (int32, error) {
|
||||
reply, err := s.AddItemRaw(ctx, serverHandle, itemDefinition)
|
||||
@@ -182,6 +201,145 @@ func (s *Session) AdviseRaw(ctx context.Context, serverHandle, itemHandle int32)
|
||||
})
|
||||
}
|
||||
|
||||
// UnAdvise invokes MXAccess UnAdvise.
|
||||
func (s *Session) UnAdvise(ctx context.Context, serverHandle, itemHandle int32) error {
|
||||
_, err := s.UnAdviseRaw(ctx, serverHandle, itemHandle)
|
||||
return err
|
||||
}
|
||||
|
||||
// UnAdviseRaw invokes MXAccess UnAdvise and returns the raw reply.
|
||||
func (s *Session) UnAdviseRaw(ctx context.Context, serverHandle, itemHandle int32) (*MxCommandReply, error) {
|
||||
return s.invokeCommand(ctx, &pb.MxCommand{
|
||||
Kind: pb.MxCommandKind_MX_COMMAND_KIND_UN_ADVISE,
|
||||
Payload: &pb.MxCommand_UnAdvise{
|
||||
UnAdvise: &pb.UnAdviseCommand{
|
||||
ServerHandle: serverHandle,
|
||||
ItemHandle: itemHandle,
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// AddItemBulk invokes MXAccess AddItem for each tag inside one gateway command.
|
||||
func (s *Session) AddItemBulk(ctx context.Context, serverHandle int32, tagAddresses []string) ([]*SubscribeResult, error) {
|
||||
if tagAddresses == nil {
|
||||
return nil, errors.New("mxgateway: tag addresses are required")
|
||||
}
|
||||
reply, err := s.invokeCommand(ctx, &pb.MxCommand{
|
||||
Kind: pb.MxCommandKind_MX_COMMAND_KIND_ADD_ITEM_BULK,
|
||||
Payload: &pb.MxCommand_AddItemBulk{
|
||||
AddItemBulk: &pb.AddItemBulkCommand{
|
||||
ServerHandle: serverHandle,
|
||||
TagAddresses: tagAddresses,
|
||||
},
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return reply.GetAddItemBulk().GetResults(), nil
|
||||
}
|
||||
|
||||
// AdviseItemBulk invokes MXAccess Advise for each item handle inside one gateway command.
|
||||
func (s *Session) AdviseItemBulk(ctx context.Context, serverHandle int32, itemHandles []int32) ([]*SubscribeResult, error) {
|
||||
if itemHandles == nil {
|
||||
return nil, errors.New("mxgateway: item handles are required")
|
||||
}
|
||||
reply, err := s.invokeCommand(ctx, &pb.MxCommand{
|
||||
Kind: pb.MxCommandKind_MX_COMMAND_KIND_ADVISE_ITEM_BULK,
|
||||
Payload: &pb.MxCommand_AdviseItemBulk{
|
||||
AdviseItemBulk: &pb.AdviseItemBulkCommand{
|
||||
ServerHandle: serverHandle,
|
||||
ItemHandles: itemHandles,
|
||||
},
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return reply.GetAdviseItemBulk().GetResults(), nil
|
||||
}
|
||||
|
||||
// RemoveItemBulk invokes MXAccess RemoveItem for each item handle inside one gateway command.
|
||||
func (s *Session) RemoveItemBulk(ctx context.Context, serverHandle int32, itemHandles []int32) ([]*SubscribeResult, error) {
|
||||
if itemHandles == nil {
|
||||
return nil, errors.New("mxgateway: item handles are required")
|
||||
}
|
||||
reply, err := s.invokeCommand(ctx, &pb.MxCommand{
|
||||
Kind: pb.MxCommandKind_MX_COMMAND_KIND_REMOVE_ITEM_BULK,
|
||||
Payload: &pb.MxCommand_RemoveItemBulk{
|
||||
RemoveItemBulk: &pb.RemoveItemBulkCommand{
|
||||
ServerHandle: serverHandle,
|
||||
ItemHandles: itemHandles,
|
||||
},
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return reply.GetRemoveItemBulk().GetResults(), nil
|
||||
}
|
||||
|
||||
// UnAdviseItemBulk invokes MXAccess UnAdvise for each item handle inside one gateway command.
|
||||
func (s *Session) UnAdviseItemBulk(ctx context.Context, serverHandle int32, itemHandles []int32) ([]*SubscribeResult, error) {
|
||||
if itemHandles == nil {
|
||||
return nil, errors.New("mxgateway: item handles are required")
|
||||
}
|
||||
reply, err := s.invokeCommand(ctx, &pb.MxCommand{
|
||||
Kind: pb.MxCommandKind_MX_COMMAND_KIND_UN_ADVISE_ITEM_BULK,
|
||||
Payload: &pb.MxCommand_UnAdviseItemBulk{
|
||||
UnAdviseItemBulk: &pb.UnAdviseItemBulkCommand{
|
||||
ServerHandle: serverHandle,
|
||||
ItemHandles: itemHandles,
|
||||
},
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return reply.GetUnAdviseItemBulk().GetResults(), nil
|
||||
}
|
||||
|
||||
// SubscribeBulk invokes AddItem and Advise for each tag inside one gateway command.
|
||||
func (s *Session) SubscribeBulk(ctx context.Context, serverHandle int32, tagAddresses []string) ([]*SubscribeResult, error) {
|
||||
if tagAddresses == nil {
|
||||
return nil, errors.New("mxgateway: tag addresses are required")
|
||||
}
|
||||
reply, err := s.invokeCommand(ctx, &pb.MxCommand{
|
||||
Kind: pb.MxCommandKind_MX_COMMAND_KIND_SUBSCRIBE_BULK,
|
||||
Payload: &pb.MxCommand_SubscribeBulk{
|
||||
SubscribeBulk: &pb.SubscribeBulkCommand{
|
||||
ServerHandle: serverHandle,
|
||||
TagAddresses: tagAddresses,
|
||||
},
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return reply.GetSubscribeBulk().GetResults(), nil
|
||||
}
|
||||
|
||||
// UnsubscribeBulk invokes UnAdvise and RemoveItem for each item handle inside one gateway command.
|
||||
func (s *Session) UnsubscribeBulk(ctx context.Context, serverHandle int32, itemHandles []int32) ([]*SubscribeResult, error) {
|
||||
if itemHandles == nil {
|
||||
return nil, errors.New("mxgateway: item handles are required")
|
||||
}
|
||||
reply, err := s.invokeCommand(ctx, &pb.MxCommand{
|
||||
Kind: pb.MxCommandKind_MX_COMMAND_KIND_UNSUBSCRIBE_BULK,
|
||||
Payload: &pb.MxCommand_UnsubscribeBulk{
|
||||
UnsubscribeBulk: &pb.UnsubscribeBulkCommand{
|
||||
ServerHandle: serverHandle,
|
||||
ItemHandles: itemHandles,
|
||||
},
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return reply.GetUnsubscribeBulk().GetResults(), nil
|
||||
}
|
||||
|
||||
// Write invokes MXAccess Write.
|
||||
func (s *Session) Write(ctx context.Context, serverHandle, itemHandle int32, value *MxValue, userID int32) error {
|
||||
_, err := s.WriteRaw(ctx, serverHandle, itemHandle, value, userID)
|
||||
|
||||
@@ -12,30 +12,40 @@ type RawEventStream = pb.MxAccessGateway_StreamEventsClient
|
||||
// Generated protobuf aliases keep raw contract access available from the public
|
||||
// mxgateway package while generated code remains under internal/generated.
|
||||
type (
|
||||
OpenSessionRequest = pb.OpenSessionRequest
|
||||
OpenSessionReply = pb.OpenSessionReply
|
||||
CloseSessionRequest = pb.CloseSessionRequest
|
||||
CloseSessionReply = pb.CloseSessionReply
|
||||
StreamEventsRequest = pb.StreamEventsRequest
|
||||
MxCommandRequest = pb.MxCommandRequest
|
||||
MxCommandReply = pb.MxCommandReply
|
||||
MxCommand = pb.MxCommand
|
||||
MxEvent = pb.MxEvent
|
||||
MxValue = pb.MxValue
|
||||
Value = pb.MxValue
|
||||
MxArray = pb.MxArray
|
||||
MxStatusProxy = pb.MxStatusProxy
|
||||
ProtocolStatus = pb.ProtocolStatus
|
||||
RegisterCommand = pb.RegisterCommand
|
||||
UnregisterCommand = pb.UnregisterCommand
|
||||
AddItemCommand = pb.AddItemCommand
|
||||
AddItem2Command = pb.AddItem2Command
|
||||
AdviseCommand = pb.AdviseCommand
|
||||
WriteCommand = pb.WriteCommand
|
||||
Write2Command = pb.Write2Command
|
||||
RegisterReply = pb.RegisterReply
|
||||
AddItemReply = pb.AddItemReply
|
||||
AddItem2Reply = pb.AddItem2Reply
|
||||
OpenSessionRequest = pb.OpenSessionRequest
|
||||
OpenSessionReply = pb.OpenSessionReply
|
||||
CloseSessionRequest = pb.CloseSessionRequest
|
||||
CloseSessionReply = pb.CloseSessionReply
|
||||
StreamEventsRequest = pb.StreamEventsRequest
|
||||
MxCommandRequest = pb.MxCommandRequest
|
||||
MxCommandReply = pb.MxCommandReply
|
||||
MxCommand = pb.MxCommand
|
||||
MxEvent = pb.MxEvent
|
||||
MxValue = pb.MxValue
|
||||
Value = pb.MxValue
|
||||
MxArray = pb.MxArray
|
||||
MxStatusProxy = pb.MxStatusProxy
|
||||
ProtocolStatus = pb.ProtocolStatus
|
||||
RegisterCommand = pb.RegisterCommand
|
||||
UnregisterCommand = pb.UnregisterCommand
|
||||
AddItemCommand = pb.AddItemCommand
|
||||
AddItem2Command = pb.AddItem2Command
|
||||
RemoveItemCommand = pb.RemoveItemCommand
|
||||
AdviseCommand = pb.AdviseCommand
|
||||
UnAdviseCommand = pb.UnAdviseCommand
|
||||
AddItemBulkCommand = pb.AddItemBulkCommand
|
||||
AdviseItemBulkCommand = pb.AdviseItemBulkCommand
|
||||
RemoveItemBulkCommand = pb.RemoveItemBulkCommand
|
||||
UnAdviseItemBulkCommand = pb.UnAdviseItemBulkCommand
|
||||
SubscribeBulkCommand = pb.SubscribeBulkCommand
|
||||
UnsubscribeBulkCommand = pb.UnsubscribeBulkCommand
|
||||
WriteCommand = pb.WriteCommand
|
||||
Write2Command = pb.Write2Command
|
||||
RegisterReply = pb.RegisterReply
|
||||
AddItemReply = pb.AddItemReply
|
||||
AddItem2Reply = pb.AddItem2Reply
|
||||
SubscribeResult = pb.SubscribeResult
|
||||
BulkSubscribeReply = pb.BulkSubscribeReply
|
||||
)
|
||||
|
||||
type (
|
||||
@@ -49,13 +59,21 @@ type (
|
||||
)
|
||||
|
||||
const (
|
||||
CommandKindRegister = pb.MxCommandKind_MX_COMMAND_KIND_REGISTER
|
||||
CommandKindUnregister = pb.MxCommandKind_MX_COMMAND_KIND_UNREGISTER
|
||||
CommandKindAddItem = pb.MxCommandKind_MX_COMMAND_KIND_ADD_ITEM
|
||||
CommandKindAddItem2 = pb.MxCommandKind_MX_COMMAND_KIND_ADD_ITEM2
|
||||
CommandKindAdvise = pb.MxCommandKind_MX_COMMAND_KIND_ADVISE
|
||||
CommandKindWrite = pb.MxCommandKind_MX_COMMAND_KIND_WRITE
|
||||
CommandKindWrite2 = pb.MxCommandKind_MX_COMMAND_KIND_WRITE2
|
||||
CommandKindRegister = pb.MxCommandKind_MX_COMMAND_KIND_REGISTER
|
||||
CommandKindUnregister = pb.MxCommandKind_MX_COMMAND_KIND_UNREGISTER
|
||||
CommandKindAddItem = pb.MxCommandKind_MX_COMMAND_KIND_ADD_ITEM
|
||||
CommandKindAddItem2 = pb.MxCommandKind_MX_COMMAND_KIND_ADD_ITEM2
|
||||
CommandKindRemoveItem = pb.MxCommandKind_MX_COMMAND_KIND_REMOVE_ITEM
|
||||
CommandKindAdvise = pb.MxCommandKind_MX_COMMAND_KIND_ADVISE
|
||||
CommandKindUnAdvise = pb.MxCommandKind_MX_COMMAND_KIND_UN_ADVISE
|
||||
CommandKindAddItemBulk = pb.MxCommandKind_MX_COMMAND_KIND_ADD_ITEM_BULK
|
||||
CommandKindAdviseItemBulk = pb.MxCommandKind_MX_COMMAND_KIND_ADVISE_ITEM_BULK
|
||||
CommandKindRemoveItemBulk = pb.MxCommandKind_MX_COMMAND_KIND_REMOVE_ITEM_BULK
|
||||
CommandKindUnAdviseItemBulk = pb.MxCommandKind_MX_COMMAND_KIND_UN_ADVISE_ITEM_BULK
|
||||
CommandKindSubscribeBulk = pb.MxCommandKind_MX_COMMAND_KIND_SUBSCRIBE_BULK
|
||||
CommandKindUnsubscribeBulk = pb.MxCommandKind_MX_COMMAND_KIND_UNSUBSCRIBE_BULK
|
||||
CommandKindWrite = pb.MxCommandKind_MX_COMMAND_KIND_WRITE
|
||||
CommandKindWrite2 = pb.MxCommandKind_MX_COMMAND_KIND_WRITE2
|
||||
|
||||
DataTypeUnknown = pb.MxDataType_MX_DATA_TYPE_UNKNOWN
|
||||
DataTypeBoolean = pb.MxDataType_MX_DATA_TYPE_BOOLEAN
|
||||
|
||||
+102
@@ -2,9 +2,12 @@ package com.dohertylan.mxgateway.client;
|
||||
|
||||
import java.security.SecureRandom;
|
||||
import java.util.HexFormat;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import mxaccess_gateway.v1.MxaccessGateway.AddItem2Command;
|
||||
import mxaccess_gateway.v1.MxaccessGateway.AddItemBulkCommand;
|
||||
import mxaccess_gateway.v1.MxaccessGateway.AddItemCommand;
|
||||
import mxaccess_gateway.v1.MxaccessGateway.AdviseItemBulkCommand;
|
||||
import mxaccess_gateway.v1.MxaccessGateway.AdviseCommand;
|
||||
import mxaccess_gateway.v1.MxaccessGateway.CloseSessionReply;
|
||||
import mxaccess_gateway.v1.MxaccessGateway.CloseSessionRequest;
|
||||
@@ -15,7 +18,14 @@ import mxaccess_gateway.v1.MxaccessGateway.MxCommandRequest;
|
||||
import mxaccess_gateway.v1.MxaccessGateway.MxValue;
|
||||
import mxaccess_gateway.v1.MxaccessGateway.OpenSessionReply;
|
||||
import mxaccess_gateway.v1.MxaccessGateway.RegisterCommand;
|
||||
import mxaccess_gateway.v1.MxaccessGateway.RemoveItemBulkCommand;
|
||||
import mxaccess_gateway.v1.MxaccessGateway.RemoveItemCommand;
|
||||
import mxaccess_gateway.v1.MxaccessGateway.StreamEventsRequest;
|
||||
import mxaccess_gateway.v1.MxaccessGateway.SubscribeBulkCommand;
|
||||
import mxaccess_gateway.v1.MxaccessGateway.SubscribeResult;
|
||||
import mxaccess_gateway.v1.MxaccessGateway.UnAdviseCommand;
|
||||
import mxaccess_gateway.v1.MxaccessGateway.UnAdviseItemBulkCommand;
|
||||
import mxaccess_gateway.v1.MxaccessGateway.UnsubscribeBulkCommand;
|
||||
import mxaccess_gateway.v1.MxaccessGateway.UnregisterCommand;
|
||||
import mxaccess_gateway.v1.MxaccessGateway.Write2Command;
|
||||
import mxaccess_gateway.v1.MxaccessGateway.WriteCommand;
|
||||
@@ -117,6 +127,19 @@ public final class MxGatewaySession implements AutoCloseable {
|
||||
.build());
|
||||
}
|
||||
|
||||
public void removeItem(int serverHandle, int itemHandle) {
|
||||
removeItemRaw(serverHandle, itemHandle);
|
||||
}
|
||||
|
||||
public MxCommandReply removeItemRaw(int serverHandle, int itemHandle) {
|
||||
return invokeCommand(MxCommand.newBuilder()
|
||||
.setKind(MxCommandKind.MX_COMMAND_KIND_REMOVE_ITEM)
|
||||
.setRemoveItem(RemoveItemCommand.newBuilder()
|
||||
.setServerHandle(serverHandle)
|
||||
.setItemHandle(itemHandle))
|
||||
.build());
|
||||
}
|
||||
|
||||
public void advise(int serverHandle, int itemHandle) {
|
||||
adviseRaw(serverHandle, itemHandle);
|
||||
}
|
||||
@@ -130,6 +153,85 @@ public final class MxGatewaySession implements AutoCloseable {
|
||||
.build());
|
||||
}
|
||||
|
||||
public void unAdvise(int serverHandle, int itemHandle) {
|
||||
unAdviseRaw(serverHandle, itemHandle);
|
||||
}
|
||||
|
||||
public MxCommandReply unAdviseRaw(int serverHandle, int itemHandle) {
|
||||
return invokeCommand(MxCommand.newBuilder()
|
||||
.setKind(MxCommandKind.MX_COMMAND_KIND_UN_ADVISE)
|
||||
.setUnAdvise(UnAdviseCommand.newBuilder()
|
||||
.setServerHandle(serverHandle)
|
||||
.setItemHandle(itemHandle))
|
||||
.build());
|
||||
}
|
||||
|
||||
public List<SubscribeResult> addItemBulk(int serverHandle, List<String> tagAddresses) {
|
||||
Objects.requireNonNull(tagAddresses, "tagAddresses");
|
||||
MxCommandReply reply = invokeCommand(MxCommand.newBuilder()
|
||||
.setKind(MxCommandKind.MX_COMMAND_KIND_ADD_ITEM_BULK)
|
||||
.setAddItemBulk(AddItemBulkCommand.newBuilder()
|
||||
.setServerHandle(serverHandle)
|
||||
.addAllTagAddresses(tagAddresses))
|
||||
.build());
|
||||
return reply.getAddItemBulk().getResultsList();
|
||||
}
|
||||
|
||||
public List<SubscribeResult> adviseItemBulk(int serverHandle, List<Integer> itemHandles) {
|
||||
Objects.requireNonNull(itemHandles, "itemHandles");
|
||||
MxCommandReply reply = invokeCommand(MxCommand.newBuilder()
|
||||
.setKind(MxCommandKind.MX_COMMAND_KIND_ADVISE_ITEM_BULK)
|
||||
.setAdviseItemBulk(AdviseItemBulkCommand.newBuilder()
|
||||
.setServerHandle(serverHandle)
|
||||
.addAllItemHandles(itemHandles))
|
||||
.build());
|
||||
return reply.getAdviseItemBulk().getResultsList();
|
||||
}
|
||||
|
||||
public List<SubscribeResult> removeItemBulk(int serverHandle, List<Integer> itemHandles) {
|
||||
Objects.requireNonNull(itemHandles, "itemHandles");
|
||||
MxCommandReply reply = invokeCommand(MxCommand.newBuilder()
|
||||
.setKind(MxCommandKind.MX_COMMAND_KIND_REMOVE_ITEM_BULK)
|
||||
.setRemoveItemBulk(RemoveItemBulkCommand.newBuilder()
|
||||
.setServerHandle(serverHandle)
|
||||
.addAllItemHandles(itemHandles))
|
||||
.build());
|
||||
return reply.getRemoveItemBulk().getResultsList();
|
||||
}
|
||||
|
||||
public List<SubscribeResult> unAdviseItemBulk(int serverHandle, List<Integer> itemHandles) {
|
||||
Objects.requireNonNull(itemHandles, "itemHandles");
|
||||
MxCommandReply reply = invokeCommand(MxCommand.newBuilder()
|
||||
.setKind(MxCommandKind.MX_COMMAND_KIND_UN_ADVISE_ITEM_BULK)
|
||||
.setUnAdviseItemBulk(UnAdviseItemBulkCommand.newBuilder()
|
||||
.setServerHandle(serverHandle)
|
||||
.addAllItemHandles(itemHandles))
|
||||
.build());
|
||||
return reply.getUnAdviseItemBulk().getResultsList();
|
||||
}
|
||||
|
||||
public List<SubscribeResult> subscribeBulk(int serverHandle, List<String> tagAddresses) {
|
||||
Objects.requireNonNull(tagAddresses, "tagAddresses");
|
||||
MxCommandReply reply = invokeCommand(MxCommand.newBuilder()
|
||||
.setKind(MxCommandKind.MX_COMMAND_KIND_SUBSCRIBE_BULK)
|
||||
.setSubscribeBulk(SubscribeBulkCommand.newBuilder()
|
||||
.setServerHandle(serverHandle)
|
||||
.addAllTagAddresses(tagAddresses))
|
||||
.build());
|
||||
return reply.getSubscribeBulk().getResultsList();
|
||||
}
|
||||
|
||||
public List<SubscribeResult> unsubscribeBulk(int serverHandle, List<Integer> itemHandles) {
|
||||
Objects.requireNonNull(itemHandles, "itemHandles");
|
||||
MxCommandReply reply = invokeCommand(MxCommand.newBuilder()
|
||||
.setKind(MxCommandKind.MX_COMMAND_KIND_UNSUBSCRIBE_BULK)
|
||||
.setUnsubscribeBulk(UnsubscribeBulkCommand.newBuilder()
|
||||
.setServerHandle(serverHandle)
|
||||
.addAllItemHandles(itemHandles))
|
||||
.build());
|
||||
return reply.getUnsubscribeBulk().getResultsList();
|
||||
}
|
||||
|
||||
public void write(int serverHandle, int itemHandle, MxValue value, int userId) {
|
||||
writeRaw(serverHandle, itemHandle, value, userId);
|
||||
}
|
||||
|
||||
+39
@@ -17,12 +17,14 @@ import io.grpc.inprocess.InProcessServerBuilder;
|
||||
import io.grpc.stub.ServerCallStreamObserver;
|
||||
import io.grpc.stub.StreamObserver;
|
||||
import java.time.Duration;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import mxaccess_gateway.v1.MxAccessGatewayGrpc;
|
||||
import mxaccess_gateway.v1.MxaccessGateway.AddItemReply;
|
||||
import mxaccess_gateway.v1.MxaccessGateway.BulkSubscribeReply;
|
||||
import mxaccess_gateway.v1.MxaccessGateway.CloseSessionReply;
|
||||
import mxaccess_gateway.v1.MxaccessGateway.CloseSessionRequest;
|
||||
import mxaccess_gateway.v1.MxaccessGateway.MxCommandKind;
|
||||
@@ -36,6 +38,7 @@ import mxaccess_gateway.v1.MxaccessGateway.ProtocolStatusCode;
|
||||
import mxaccess_gateway.v1.MxaccessGateway.RegisterReply;
|
||||
import mxaccess_gateway.v1.MxaccessGateway.SessionState;
|
||||
import mxaccess_gateway.v1.MxaccessGateway.StreamEventsRequest;
|
||||
import mxaccess_gateway.v1.MxaccessGateway.SubscribeResult;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
final class MxGatewayClientSessionTests {
|
||||
@@ -112,6 +115,42 @@ final class MxGatewayClientSessionTests {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void subscribeBulkBuildsOneBulkCommandAndReturnsResults() throws Exception {
|
||||
AtomicReference<MxCommandRequest> commandRequest = new AtomicReference<>();
|
||||
TestGatewayService service = new TestGatewayService() {
|
||||
@Override
|
||||
public void invoke(MxCommandRequest request, StreamObserver<MxCommandReply> responseObserver) {
|
||||
commandRequest.set(request);
|
||||
responseObserver.onNext(MxCommandReply.newBuilder()
|
||||
.setSessionId(request.getSessionId())
|
||||
.setKind(request.getCommand().getKind())
|
||||
.setProtocolStatus(ok())
|
||||
.setSubscribeBulk(BulkSubscribeReply.newBuilder()
|
||||
.addResults(SubscribeResult.newBuilder()
|
||||
.setServerHandle(12)
|
||||
.setTagAddress("Area001.Pump001.Speed")
|
||||
.setItemHandle(34)
|
||||
.setWasSuccessful(true)))
|
||||
.build());
|
||||
responseObserver.onCompleted();
|
||||
}
|
||||
};
|
||||
|
||||
try (InProcessGateway gateway = InProcessGateway.start(service, new AtomicReference<>());
|
||||
MxGatewayClient client = gateway.client("", Duration.ofSeconds(5))) {
|
||||
MxGatewaySession session = MxGatewaySession.forSessionId(client, "existing-session");
|
||||
|
||||
List<SubscribeResult> results = session.subscribeBulk(12, List.of("Area001.Pump001.Speed"));
|
||||
|
||||
assertEquals(34, results.get(0).getItemHandle());
|
||||
assertEquals(MxCommandKind.MX_COMMAND_KIND_SUBSCRIBE_BULK, commandRequest.get().getCommand().getKind());
|
||||
assertEquals(
|
||||
List.of("Area001.Pump001.Speed"),
|
||||
commandRequest.get().getCommand().getSubscribeBulk().getTagAddressesList());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void streamCancellationCancelsServerCall() throws Exception {
|
||||
CountDownLatch cancelled = new CountDownLatch(1);
|
||||
|
||||
+8568
-293
File diff suppressed because it is too large
Load Diff
Binary file not shown.
File diff suppressed because one or more lines are too long
@@ -2,7 +2,7 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from collections.abc import AsyncIterator
|
||||
from collections.abc import AsyncIterator, Sequence
|
||||
|
||||
from .errors import ensure_mxaccess_success
|
||||
from .generated import mxaccess_gateway_pb2 as pb
|
||||
@@ -89,6 +89,24 @@ class Session:
|
||||
correlation_id=correlation_id,
|
||||
)
|
||||
|
||||
async def remove_item(
|
||||
self,
|
||||
server_handle: int,
|
||||
item_handle: int,
|
||||
*,
|
||||
correlation_id: str = "",
|
||||
) -> None:
|
||||
await self.invoke(
|
||||
pb.MxCommand(
|
||||
kind=pb.MX_COMMAND_KIND_REMOVE_ITEM,
|
||||
remove_item=pb.RemoveItemCommand(
|
||||
server_handle=server_handle,
|
||||
item_handle=item_handle,
|
||||
),
|
||||
),
|
||||
correlation_id=correlation_id,
|
||||
)
|
||||
|
||||
async def add_item(
|
||||
self,
|
||||
server_handle: int,
|
||||
@@ -147,6 +165,150 @@ class Session:
|
||||
correlation_id=correlation_id,
|
||||
)
|
||||
|
||||
async def unadvise(
|
||||
self,
|
||||
server_handle: int,
|
||||
item_handle: int,
|
||||
*,
|
||||
correlation_id: str = "",
|
||||
) -> None:
|
||||
await self.invoke(
|
||||
pb.MxCommand(
|
||||
kind=pb.MX_COMMAND_KIND_UN_ADVISE,
|
||||
un_advise=pb.UnAdviseCommand(
|
||||
server_handle=server_handle,
|
||||
item_handle=item_handle,
|
||||
),
|
||||
),
|
||||
correlation_id=correlation_id,
|
||||
)
|
||||
|
||||
async def add_item_bulk(
|
||||
self,
|
||||
server_handle: int,
|
||||
tag_addresses: Sequence[str],
|
||||
*,
|
||||
correlation_id: str = "",
|
||||
) -> list[pb.SubscribeResult]:
|
||||
if tag_addresses is None:
|
||||
raise TypeError("tag_addresses is required")
|
||||
reply = await self.invoke(
|
||||
pb.MxCommand(
|
||||
kind=pb.MX_COMMAND_KIND_ADD_ITEM_BULK,
|
||||
add_item_bulk=pb.AddItemBulkCommand(
|
||||
server_handle=server_handle,
|
||||
tag_addresses=tag_addresses,
|
||||
),
|
||||
),
|
||||
correlation_id=correlation_id,
|
||||
)
|
||||
return list(reply.add_item_bulk.results)
|
||||
|
||||
async def advise_item_bulk(
|
||||
self,
|
||||
server_handle: int,
|
||||
item_handles: Sequence[int],
|
||||
*,
|
||||
correlation_id: str = "",
|
||||
) -> list[pb.SubscribeResult]:
|
||||
if item_handles is None:
|
||||
raise TypeError("item_handles is required")
|
||||
reply = await self.invoke(
|
||||
pb.MxCommand(
|
||||
kind=pb.MX_COMMAND_KIND_ADVISE_ITEM_BULK,
|
||||
advise_item_bulk=pb.AdviseItemBulkCommand(
|
||||
server_handle=server_handle,
|
||||
item_handles=item_handles,
|
||||
),
|
||||
),
|
||||
correlation_id=correlation_id,
|
||||
)
|
||||
return list(reply.advise_item_bulk.results)
|
||||
|
||||
async def remove_item_bulk(
|
||||
self,
|
||||
server_handle: int,
|
||||
item_handles: Sequence[int],
|
||||
*,
|
||||
correlation_id: str = "",
|
||||
) -> list[pb.SubscribeResult]:
|
||||
if item_handles is None:
|
||||
raise TypeError("item_handles is required")
|
||||
reply = await self.invoke(
|
||||
pb.MxCommand(
|
||||
kind=pb.MX_COMMAND_KIND_REMOVE_ITEM_BULK,
|
||||
remove_item_bulk=pb.RemoveItemBulkCommand(
|
||||
server_handle=server_handle,
|
||||
item_handles=item_handles,
|
||||
),
|
||||
),
|
||||
correlation_id=correlation_id,
|
||||
)
|
||||
return list(reply.remove_item_bulk.results)
|
||||
|
||||
async def unadvise_item_bulk(
|
||||
self,
|
||||
server_handle: int,
|
||||
item_handles: Sequence[int],
|
||||
*,
|
||||
correlation_id: str = "",
|
||||
) -> list[pb.SubscribeResult]:
|
||||
if item_handles is None:
|
||||
raise TypeError("item_handles is required")
|
||||
reply = await self.invoke(
|
||||
pb.MxCommand(
|
||||
kind=pb.MX_COMMAND_KIND_UN_ADVISE_ITEM_BULK,
|
||||
un_advise_item_bulk=pb.UnAdviseItemBulkCommand(
|
||||
server_handle=server_handle,
|
||||
item_handles=item_handles,
|
||||
),
|
||||
),
|
||||
correlation_id=correlation_id,
|
||||
)
|
||||
return list(reply.un_advise_item_bulk.results)
|
||||
|
||||
async def subscribe_bulk(
|
||||
self,
|
||||
server_handle: int,
|
||||
tag_addresses: Sequence[str],
|
||||
*,
|
||||
correlation_id: str = "",
|
||||
) -> list[pb.SubscribeResult]:
|
||||
if tag_addresses is None:
|
||||
raise TypeError("tag_addresses is required")
|
||||
reply = await self.invoke(
|
||||
pb.MxCommand(
|
||||
kind=pb.MX_COMMAND_KIND_SUBSCRIBE_BULK,
|
||||
subscribe_bulk=pb.SubscribeBulkCommand(
|
||||
server_handle=server_handle,
|
||||
tag_addresses=tag_addresses,
|
||||
),
|
||||
),
|
||||
correlation_id=correlation_id,
|
||||
)
|
||||
return list(reply.subscribe_bulk.results)
|
||||
|
||||
async def unsubscribe_bulk(
|
||||
self,
|
||||
server_handle: int,
|
||||
item_handles: Sequence[int],
|
||||
*,
|
||||
correlation_id: str = "",
|
||||
) -> list[pb.SubscribeResult]:
|
||||
if item_handles is None:
|
||||
raise TypeError("item_handles is required")
|
||||
reply = await self.invoke(
|
||||
pb.MxCommand(
|
||||
kind=pb.MX_COMMAND_KIND_UNSUBSCRIBE_BULK,
|
||||
unsubscribe_bulk=pb.UnsubscribeBulkCommand(
|
||||
server_handle=server_handle,
|
||||
item_handles=item_handles,
|
||||
),
|
||||
),
|
||||
correlation_id=correlation_id,
|
||||
)
|
||||
return list(reply.unsubscribe_bulk.results)
|
||||
|
||||
async def write(
|
||||
self,
|
||||
server_handle: int,
|
||||
|
||||
@@ -58,6 +58,41 @@ async def test_mxaccess_error_preserves_raw_reply() -> None:
|
||||
assert captured.value.raw_reply is failure_reply
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_subscribe_bulk_sends_one_bulk_command_and_returns_results() -> None:
|
||||
stub = FakeGatewayStub()
|
||||
bulk_reply = pb.MxCommandReply(
|
||||
session_id="session-1",
|
||||
kind=pb.MX_COMMAND_KIND_SUBSCRIBE_BULK,
|
||||
protocol_status=pb.ProtocolStatus(code=pb.PROTOCOL_STATUS_CODE_OK),
|
||||
subscribe_bulk=pb.BulkSubscribeReply(
|
||||
results=[
|
||||
pb.SubscribeResult(
|
||||
server_handle=12,
|
||||
tag_address="Area001.Pump001.Speed",
|
||||
item_handle=34,
|
||||
was_successful=True,
|
||||
),
|
||||
],
|
||||
),
|
||||
)
|
||||
stub.invoke.replies = [bulk_reply]
|
||||
client = await GatewayClient.connect(
|
||||
ClientOptions(endpoint="fake", api_key="mxgw_test_secret", plaintext=True),
|
||||
stub=stub,
|
||||
)
|
||||
session = await client.open_session()
|
||||
|
||||
results = await session.subscribe_bulk(12, ["Area001.Pump001.Speed"])
|
||||
|
||||
assert results[0].item_handle == 34
|
||||
assert len(stub.invoke.requests) == 1
|
||||
assert stub.invoke.requests[0].command.kind == pb.MX_COMMAND_KIND_SUBSCRIBE_BULK
|
||||
assert list(stub.invoke.requests[0].command.subscribe_bulk.tag_addresses) == [
|
||||
"Area001.Pump001.Speed",
|
||||
]
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_stream_events_cancels_underlying_call_when_closed() -> None:
|
||||
stream = FakeStream(
|
||||
|
||||
+172
-3
@@ -3,9 +3,11 @@ use crate::error::Error;
|
||||
use crate::generated::mxaccess_gateway::v1::mx_command::Payload;
|
||||
use crate::generated::mxaccess_gateway::v1::mx_command_reply;
|
||||
use crate::generated::mxaccess_gateway::v1::{
|
||||
AddItem2Command, AddItemCommand, AdviseCommand, CloseSessionRequest, MxCommand, MxCommandKind,
|
||||
MxCommandReply, MxCommandRequest, MxValue as ProtoMxValue, OpenSessionRequest, RegisterCommand,
|
||||
StreamEventsRequest, Write2Command, WriteCommand,
|
||||
AddItem2Command, AddItemBulkCommand, AddItemCommand, AdviseCommand, AdviseItemBulkCommand,
|
||||
CloseSessionRequest, MxCommand, MxCommandKind, MxCommandReply, MxCommandRequest,
|
||||
MxValue as ProtoMxValue, OpenSessionRequest, RegisterCommand, RemoveItemBulkCommand,
|
||||
RemoveItemCommand, StreamEventsRequest, SubscribeBulkCommand, SubscribeResult, UnAdviseCommand,
|
||||
UnAdviseItemBulkCommand, UnsubscribeBulkCommand, Write2Command, WriteCommand,
|
||||
};
|
||||
use crate::value::MxValue;
|
||||
|
||||
@@ -94,6 +96,18 @@ impl Session {
|
||||
Ok(add_item2_handle(&reply))
|
||||
}
|
||||
|
||||
pub async fn remove_item(&self, server_handle: i32, item_handle: i32) -> Result<(), Error> {
|
||||
self.invoke(
|
||||
MxCommandKind::RemoveItem,
|
||||
Payload::RemoveItem(RemoveItemCommand {
|
||||
server_handle,
|
||||
item_handle,
|
||||
}),
|
||||
)
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn advise(&self, server_handle: i32, item_handle: i32) -> Result<(), Error> {
|
||||
self.invoke(
|
||||
MxCommandKind::Advise,
|
||||
@@ -106,6 +120,126 @@ impl Session {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn un_advise(&self, server_handle: i32, item_handle: i32) -> Result<(), Error> {
|
||||
self.invoke(
|
||||
MxCommandKind::UnAdvise,
|
||||
Payload::UnAdvise(UnAdviseCommand {
|
||||
server_handle,
|
||||
item_handle,
|
||||
}),
|
||||
)
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn add_item_bulk(
|
||||
&self,
|
||||
server_handle: i32,
|
||||
tag_addresses: Vec<String>,
|
||||
) -> Result<Vec<SubscribeResult>, Error> {
|
||||
let reply = self
|
||||
.invoke(
|
||||
MxCommandKind::AddItemBulk,
|
||||
Payload::AddItemBulk(AddItemBulkCommand {
|
||||
server_handle,
|
||||
tag_addresses,
|
||||
}),
|
||||
)
|
||||
.await?;
|
||||
|
||||
Ok(bulk_results(reply, BulkReplyKind::AddItemBulk))
|
||||
}
|
||||
|
||||
pub async fn advise_item_bulk(
|
||||
&self,
|
||||
server_handle: i32,
|
||||
item_handles: Vec<i32>,
|
||||
) -> Result<Vec<SubscribeResult>, Error> {
|
||||
let reply = self
|
||||
.invoke(
|
||||
MxCommandKind::AdviseItemBulk,
|
||||
Payload::AdviseItemBulk(AdviseItemBulkCommand {
|
||||
server_handle,
|
||||
item_handles,
|
||||
}),
|
||||
)
|
||||
.await?;
|
||||
|
||||
Ok(bulk_results(reply, BulkReplyKind::AdviseItemBulk))
|
||||
}
|
||||
|
||||
pub async fn remove_item_bulk(
|
||||
&self,
|
||||
server_handle: i32,
|
||||
item_handles: Vec<i32>,
|
||||
) -> Result<Vec<SubscribeResult>, Error> {
|
||||
let reply = self
|
||||
.invoke(
|
||||
MxCommandKind::RemoveItemBulk,
|
||||
Payload::RemoveItemBulk(RemoveItemBulkCommand {
|
||||
server_handle,
|
||||
item_handles,
|
||||
}),
|
||||
)
|
||||
.await?;
|
||||
|
||||
Ok(bulk_results(reply, BulkReplyKind::RemoveItemBulk))
|
||||
}
|
||||
|
||||
pub async fn un_advise_item_bulk(
|
||||
&self,
|
||||
server_handle: i32,
|
||||
item_handles: Vec<i32>,
|
||||
) -> Result<Vec<SubscribeResult>, Error> {
|
||||
let reply = self
|
||||
.invoke(
|
||||
MxCommandKind::UnAdviseItemBulk,
|
||||
Payload::UnAdviseItemBulk(UnAdviseItemBulkCommand {
|
||||
server_handle,
|
||||
item_handles,
|
||||
}),
|
||||
)
|
||||
.await?;
|
||||
|
||||
Ok(bulk_results(reply, BulkReplyKind::UnAdviseItemBulk))
|
||||
}
|
||||
|
||||
pub async fn subscribe_bulk(
|
||||
&self,
|
||||
server_handle: i32,
|
||||
tag_addresses: Vec<String>,
|
||||
) -> Result<Vec<SubscribeResult>, Error> {
|
||||
let reply = self
|
||||
.invoke(
|
||||
MxCommandKind::SubscribeBulk,
|
||||
Payload::SubscribeBulk(SubscribeBulkCommand {
|
||||
server_handle,
|
||||
tag_addresses,
|
||||
}),
|
||||
)
|
||||
.await?;
|
||||
|
||||
Ok(bulk_results(reply, BulkReplyKind::SubscribeBulk))
|
||||
}
|
||||
|
||||
pub async fn unsubscribe_bulk(
|
||||
&self,
|
||||
server_handle: i32,
|
||||
item_handles: Vec<i32>,
|
||||
) -> Result<Vec<SubscribeResult>, Error> {
|
||||
let reply = self
|
||||
.invoke(
|
||||
MxCommandKind::UnsubscribeBulk,
|
||||
Payload::UnsubscribeBulk(UnsubscribeBulkCommand {
|
||||
server_handle,
|
||||
item_handles,
|
||||
}),
|
||||
)
|
||||
.await?;
|
||||
|
||||
Ok(bulk_results(reply, BulkReplyKind::UnsubscribeBulk))
|
||||
}
|
||||
|
||||
pub async fn write(
|
||||
&self,
|
||||
server_handle: i32,
|
||||
@@ -226,6 +360,41 @@ fn add_item2_handle(reply: &MxCommandReply) -> i32 {
|
||||
}
|
||||
}
|
||||
|
||||
enum BulkReplyKind {
|
||||
AddItemBulk,
|
||||
AdviseItemBulk,
|
||||
RemoveItemBulk,
|
||||
UnAdviseItemBulk,
|
||||
SubscribeBulk,
|
||||
UnsubscribeBulk,
|
||||
}
|
||||
|
||||
fn bulk_results(reply: MxCommandReply, kind: BulkReplyKind) -> Vec<SubscribeResult> {
|
||||
match (reply.payload, kind) {
|
||||
(Some(mx_command_reply::Payload::AddItemBulk(reply)), BulkReplyKind::AddItemBulk) => {
|
||||
reply.results
|
||||
}
|
||||
(Some(mx_command_reply::Payload::AdviseItemBulk(reply)), BulkReplyKind::AdviseItemBulk) => {
|
||||
reply.results
|
||||
}
|
||||
(Some(mx_command_reply::Payload::RemoveItemBulk(reply)), BulkReplyKind::RemoveItemBulk) => {
|
||||
reply.results
|
||||
}
|
||||
(
|
||||
Some(mx_command_reply::Payload::UnAdviseItemBulk(reply)),
|
||||
BulkReplyKind::UnAdviseItemBulk,
|
||||
) => reply.results,
|
||||
(Some(mx_command_reply::Payload::SubscribeBulk(reply)), BulkReplyKind::SubscribeBulk) => {
|
||||
reply.results
|
||||
}
|
||||
(
|
||||
Some(mx_command_reply::Payload::UnsubscribeBulk(reply)),
|
||||
BulkReplyKind::UnsubscribeBulk,
|
||||
) => reply.results,
|
||||
_ => Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
fn int32_reply_value(value: &ProtoMxValue) -> Option<i32> {
|
||||
match value.kind.as_ref()? {
|
||||
crate::generated::mxaccess_gateway::v1::mx_value::Kind::Int32Value(value) => Some(*value),
|
||||
|
||||
@@ -14,10 +14,10 @@ use mxgateway_client::generated::mxaccess_gateway::v1::mx_access_gateway_server:
|
||||
use mxgateway_client::generated::mxaccess_gateway::v1::mx_command_reply;
|
||||
use mxgateway_client::generated::mxaccess_gateway::v1::mx_value::Kind;
|
||||
use mxgateway_client::generated::mxaccess_gateway::v1::{
|
||||
AddItemReply, CloseSessionReply, CloseSessionRequest, MxCommandKind, MxCommandReply,
|
||||
MxDataType, MxEvent, MxEventFamily, MxStatusCategory, MxStatusProxy, MxStatusSource, MxValue,
|
||||
OpenSessionReply, OpenSessionRequest, ProtocolStatus, ProtocolStatusCode, SessionState,
|
||||
StreamEventsRequest,
|
||||
AddItemReply, BulkSubscribeReply, CloseSessionReply, CloseSessionRequest, MxCommandKind,
|
||||
MxCommandReply, MxDataType, MxEvent, MxEventFamily, MxStatusCategory, MxStatusProxy,
|
||||
MxStatusSource, MxValue, OpenSessionReply, OpenSessionRequest, ProtocolStatus,
|
||||
ProtocolStatusCode, SessionState, StreamEventsRequest, SubscribeResult,
|
||||
};
|
||||
use mxgateway_client::{
|
||||
ApiKey, ClientOptions, CommandError, Error, GatewayClient, MxStatus, MxValue as ClientMxValue,
|
||||
@@ -87,6 +87,25 @@ async fn session_helpers_build_commands_and_preserve_command_errors() {
|
||||
assert_eq!(error.reply().statuses.len(), 2);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn subscribe_bulk_builds_one_bulk_command_and_returns_results() {
|
||||
let state = Arc::new(FakeState::default());
|
||||
let endpoint = spawn_fake_gateway(state.clone()).await;
|
||||
let client = GatewayClient::connect(ClientOptions::new(endpoint))
|
||||
.await
|
||||
.unwrap();
|
||||
let session = client.session("session-fixture");
|
||||
|
||||
let results = session
|
||||
.subscribe_bulk(12, vec!["Area001.Pump001.Speed".to_owned()])
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(results[0].item_handle, 34);
|
||||
let last_command = state.last_command_kind.lock().await;
|
||||
assert_eq!(*last_command, Some(MxCommandKind::SubscribeBulk as i32));
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn event_stream_preserves_order_and_drop_cancels_server_stream() {
|
||||
let state = Arc::new(FakeState::default());
|
||||
@@ -268,6 +287,27 @@ impl MxAccessGateway for FakeGateway {
|
||||
return Ok(Response::new(mxaccess_failure_reply()));
|
||||
}
|
||||
|
||||
if kind == MxCommandKind::SubscribeBulk as i32 {
|
||||
return Ok(Response::new(MxCommandReply {
|
||||
session_id: request.session_id,
|
||||
correlation_id: "fake-correlation".to_owned(),
|
||||
kind,
|
||||
protocol_status: Some(ok_status("command ok")),
|
||||
payload: Some(mx_command_reply::Payload::SubscribeBulk(
|
||||
BulkSubscribeReply {
|
||||
results: vec![SubscribeResult {
|
||||
server_handle: 12,
|
||||
tag_address: "Area001.Pump001.Speed".to_owned(),
|
||||
item_handle: 34,
|
||||
was_successful: true,
|
||||
error_message: String::new(),
|
||||
}],
|
||||
},
|
||||
)),
|
||||
..MxCommandReply::default()
|
||||
}));
|
||||
}
|
||||
|
||||
Ok(Response::new(MxCommandReply {
|
||||
session_id: request.session_id,
|
||||
correlation_id: "fake-correlation".to_owned(),
|
||||
|
||||
Reference in New Issue
Block a user