diff --git a/docs/plans/2026-09-05-flow-debugger-design.md b/docs/plans/2026-09-05-flow-debugger-design.md index a1c1ab35..1ab66902 100644 --- a/docs/plans/2026-09-05-flow-debugger-design.md +++ b/docs/plans/2026-09-05-flow-debugger-design.md @@ -170,7 +170,11 @@ attaches the list to the `ScriptCallResult` → `RouteToCallResponse` when `Trac collector applies `IAuditRedactor` inside `Add`, so the inline record is the same filtered event the writer persists, and it caps at `MaxTraceEventsPerRun` (500) **or** 1 MB of summed `DetailsJson` (`DefaultMaxBytes`), whichever comes first; beyond either bound it keeps the earlier -events, sets `Truncated`, and the audit rows remain complete (arch-review F1). A site on an older build ignores +events, sets `Truncated`, and the audit rows remain complete (arch-review F1). The site collector's +`Truncated`/`LogsTruncated` ride back on `ScriptCallResult` → `RouteToCallResponse` +(`RouteToCallResponseDto` fields 8/9, Gitea #54) and `FlowDebugRunner` ORs them into +`FlowDebugRunResult` on both the script entry and (through `RouteHelper.WithTraceSink`'s truncation +sink) the method entry, so a short site lane is labelled truncated. A site on an older build ignores `Trace`, returns no `TraceEvents`, and the UI labels its lane `site not traced (older build)`. `FlowStep` is a **central-side projection** (`Commons/Types/Debugging/FlowStepProjector`) computed from `AuditEvent` + `AuditDetails`; nothing serializes it. diff --git a/src/ZB.MOM.WW.ScadaBridge.Commons/Messages/InboundApi/RouteToInstanceRequest.cs b/src/ZB.MOM.WW.ScadaBridge.Commons/Messages/InboundApi/RouteToInstanceRequest.cs index bea0483b..359fc4d6 100644 --- a/src/ZB.MOM.WW.ScadaBridge.Commons/Messages/InboundApi/RouteToInstanceRequest.cs +++ b/src/ZB.MOM.WW.ScadaBridge.Commons/Messages/InboundApi/RouteToInstanceRequest.cs @@ -29,6 +29,8 @@ public record RouteToCallRequest( /// /// Flow Debugger — the routed site run's ExecutionId (design 2026-09-05 §5.4); null from a site that predates the field. /// Flow Debugger — the site's ScriptTrace audit events for a traced run, in emission order (design 2026-09-05 §5.4); null when untraced or from an older site. +/// Flow Debugger (Gitea #54) — the site's inline collector dropped step rows, so is short. False when untraced or from an older site. +/// Flow Debugger (Gitea #54) — the site's inline collector dropped log-line rows. False when untraced or from an older site. public record RouteToCallResponse( string CorrelationId, bool Success, @@ -36,7 +38,9 @@ public record RouteToCallResponse( string? ErrorMessage, DateTimeOffset Timestamp, Guid? ChildExecutionId = null, - IReadOnlyList? TraceEvents = null); + IReadOnlyList? TraceEvents = null, + bool Truncated = false, + bool LogsTruncated = false); /// /// Request to read attribute(s) from a remote instance. diff --git a/src/ZB.MOM.WW.ScadaBridge.Commons/Messages/ScriptExecution/ScriptCallResult.cs b/src/ZB.MOM.WW.ScadaBridge.Commons/Messages/ScriptExecution/ScriptCallResult.cs index d506c357..d5ca4092 100644 --- a/src/ZB.MOM.WW.ScadaBridge.Commons/Messages/ScriptExecution/ScriptCallResult.cs +++ b/src/ZB.MOM.WW.ScadaBridge.Commons/Messages/ScriptExecution/ScriptCallResult.cs @@ -4,10 +4,14 @@ namespace ZB.MOM.WW.ScadaBridge.Commons.Messages.ScriptExecution; /// Flow Debugger — the run's own ExecutionId (design 2026-09-05 §5.4). Additive; null from callers that predate the field. /// Flow Debugger — the run's ScriptTrace audit events in emission order when traced (design 2026-09-05 §5.4); null otherwise. +/// Flow Debugger (Gitea #54) — the site's inline trace collector dropped a step row (event cap or byte budget), so is short. The audit rows are complete; Replay shows everything. Additive; false when untraced. +/// Flow Debugger (Gitea #54) — the site's collector dropped low-priority log-line rows to keep room for the step rows. Additive; false when untraced. public record ScriptCallResult( string CorrelationId, bool Success, object? ReturnValue, string? ErrorMessage, Guid? ExecutionId = null, - IReadOnlyList? TraceEvents = null); + IReadOnlyList? TraceEvents = null, + bool Truncated = false, + bool LogsTruncated = false); diff --git a/src/ZB.MOM.WW.ScadaBridge.Communication/Grpc/SiteCommandDtoMapper.cs b/src/ZB.MOM.WW.ScadaBridge.Communication/Grpc/SiteCommandDtoMapper.cs index cc832a25..ca03f534 100644 --- a/src/ZB.MOM.WW.ScadaBridge.Communication/Grpc/SiteCommandDtoMapper.cs +++ b/src/ZB.MOM.WW.ScadaBridge.Communication/Grpc/SiteCommandDtoMapper.cs @@ -2649,7 +2649,10 @@ public static class SiteCommandDtoMapper ErrorMessage = OrEmpty(response.ErrorMessage), Timestamp = Ts(response.Timestamp), // Flow Debugger (design 2026-09-05 §5.4): additive fields 6/7. - ChildExecutionId = GuidToWire(response.ChildExecutionId) + ChildExecutionId = GuidToWire(response.ChildExecutionId), + // Gitea #54: additive fields 8/9 — the site collector's truncation flags. + Truncated = response.Truncated, + LogsTruncated = response.LogsTruncated }; if (response.TraceEvents is { Count: > 0 } traceEvents) { @@ -2677,7 +2680,9 @@ public static class SiteCommandDtoMapper GuidFromWire(dto.ChildExecutionId), // An untraced run — or an older site that never sets the field — sends no // events, which maps to null so the UI can label the lane "site not traced". - dto.TraceEvents.Count == 0 ? null : dto.TraceEvents.Select(AuditEventDtoMapper.FromDto).ToList()); + dto.TraceEvents.Count == 0 ? null : dto.TraceEvents.Select(AuditEventDtoMapper.FromDto).ToList(), + dto.Truncated, + dto.LogsTruncated); } /// Projects a onto the wire. diff --git a/src/ZB.MOM.WW.ScadaBridge.Communication/Protos/site_command.proto b/src/ZB.MOM.WW.ScadaBridge.Communication/Protos/site_command.proto index 585f0070..8cb3bdcf 100644 --- a/src/ZB.MOM.WW.ScadaBridge.Communication/Protos/site_command.proto +++ b/src/ZB.MOM.WW.ScadaBridge.Communication/Protos/site_command.proto @@ -810,6 +810,10 @@ message RouteToCallResponseDto { // Flow Debugger (design §5.4): the site's ScriptTrace audit events for a traced run, // in emission order. Empty from an untraced run or an older site (mapper => null). repeated sitestream.AuditEventDto trace_events = 7; + // Flow Debugger (Gitea #54): the site's inline trace collector dropped step rows (truncated) + // or log-line rows (logs_truncated). proto3 default false from an untraced run or older site. + bool truncated = 8; + bool logs_truncated = 9; } message RouteToGetAttributesRequestDto { diff --git a/src/ZB.MOM.WW.ScadaBridge.Communication/SiteCommandGrpc/SiteCommand.cs b/src/ZB.MOM.WW.ScadaBridge.Communication/SiteCommandGrpc/SiteCommand.cs index 8c371b69..cc64edeb 100644 --- a/src/ZB.MOM.WW.ScadaBridge.Communication/SiteCommandGrpc/SiteCommand.cs +++ b/src/ZB.MOM.WW.ScadaBridge.Communication/SiteCommandGrpc/SiteCommand.cs @@ -388,132 +388,133 @@ namespace ZB.MOM.WW.ScadaBridge.Communication.Grpc { "KAkSEwoLc2NyaXB0X25hbWUYAyABKAkSPQoKcGFyYW1ldGVycxgEIAEoCzIp", "LnNjYWRhYnJpZGdlLnNpdGVjb21tYW5kLnYxLkxvb3NlVmFsdWVNYXASLQoJ", "dGltZXN0YW1wGAUgASgLMhouZ29vZ2xlLnByb3RvYnVmLlRpbWVzdGFtcBIb", - "ChNwYXJlbnRfZXhlY3V0aW9uX2lkGAYgASgJEg0KBXRyYWNlGAcgASgIIpIC", + "ChNwYXJlbnRfZXhlY3V0aW9uX2lkGAYgASgJEg0KBXRyYWNlGAcgASgIIr0C", "ChZSb3V0ZVRvQ2FsbFJlc3BvbnNlRHRvEhYKDmNvcnJlbGF0aW9uX2lkGAEg", "ASgJEg8KB3N1Y2Nlc3MYAiABKAgSPAoMcmV0dXJuX3ZhbHVlGAMgASgLMiYu", "c2NhZGFicmlkZ2Uuc2l0ZWNvbW1hbmQudjEuTG9vc2VWYWx1ZRIVCg1lcnJv", "cl9tZXNzYWdlGAQgASgJEi0KCXRpbWVzdGFtcBgFIAEoCzIaLmdvb2dsZS5w", "cm90b2J1Zi5UaW1lc3RhbXASGgoSY2hpbGRfZXhlY3V0aW9uX2lkGAYgASgJ", "Ei8KDHRyYWNlX2V2ZW50cxgHIAMoCzIZLnNpdGVzdHJlYW0uQXVkaXRFdmVu", - "dER0byK7AQoeUm91dGVUb0dldEF0dHJpYnV0ZXNSZXF1ZXN0RHRvEhYKDmNv", - "cnJlbGF0aW9uX2lkGAEgASgJEhwKFGluc3RhbmNlX3VuaXF1ZV9uYW1lGAIg", - "ASgJEhcKD2F0dHJpYnV0ZV9uYW1lcxgDIAMoCRItCgl0aW1lc3RhbXAYBCAB", + "dER0bxIRCgl0cnVuY2F0ZWQYCCABKAgSFgoObG9nc190cnVuY2F0ZWQYCSAB", + "KAgiuwEKHlJvdXRlVG9HZXRBdHRyaWJ1dGVzUmVxdWVzdER0bxIWCg5jb3Jy", + "ZWxhdGlvbl9pZBgBIAEoCRIcChRpbnN0YW5jZV91bmlxdWVfbmFtZRgCIAEo", + "CRIXCg9hdHRyaWJ1dGVfbmFtZXMYAyADKAkSLQoJdGltZXN0YW1wGAQgASgL", + "MhouZ29vZ2xlLnByb3RvYnVmLlRpbWVzdGFtcBIbChNwYXJlbnRfZXhlY3V0", + "aW9uX2lkGAUgASgJIssBCh9Sb3V0ZVRvR2V0QXR0cmlidXRlc1Jlc3BvbnNl", + "RHRvEhYKDmNvcnJlbGF0aW9uX2lkGAEgASgJEjkKBnZhbHVlcxgCIAEoCzIp", + "LnNjYWRhYnJpZGdlLnNpdGVjb21tYW5kLnYxLkxvb3NlVmFsdWVNYXASDwoH", + "c3VjY2VzcxgDIAEoCBIVCg1lcnJvcl9tZXNzYWdlGAQgASgJEi0KCXRpbWVz", + "dGFtcBgFIAEoCzIaLmdvb2dsZS5wcm90b2J1Zi5UaW1lc3RhbXAi1AIKHlJv", + "dXRlVG9TZXRBdHRyaWJ1dGVzUmVxdWVzdER0bxIWCg5jb3JyZWxhdGlvbl9p", + "ZBgBIAEoCRIcChRpbnN0YW5jZV91bmlxdWVfbmFtZRgCIAEoCRJpChBhdHRy", + "aWJ1dGVfdmFsdWVzGAMgAygLMk8uc2NhZGFicmlkZ2Uuc2l0ZWNvbW1hbmQu", + "djEuUm91dGVUb1NldEF0dHJpYnV0ZXNSZXF1ZXN0RHRvLkF0dHJpYnV0ZVZh", + "bHVlc0VudHJ5Ei0KCXRpbWVzdGFtcBgEIAEoCzIaLmdvb2dsZS5wcm90b2J1", + "Zi5UaW1lc3RhbXASGwoTcGFyZW50X2V4ZWN1dGlvbl9pZBgFIAEoCRINCgV0", + "cmFjZRgGIAEoCBo2ChRBdHRyaWJ1dGVWYWx1ZXNFbnRyeRILCgNrZXkYASAB", + "KAkSDQoFdmFsdWUYAiABKAk6AjgBIpABCh9Sb3V0ZVRvU2V0QXR0cmlidXRl", + "c1Jlc3BvbnNlRHRvEhYKDmNvcnJlbGF0aW9uX2lkGAEgASgJEg8KB3N1Y2Nl", + "c3MYAiABKAgSFQoNZXJyb3JfbWVzc2FnZRgDIAEoCRItCgl0aW1lc3RhbXAY", + "BCABKAsyGi5nb29nbGUucHJvdG9idWYuVGltZXN0YW1wIsMCCiFSb3V0ZVRv", + "V2FpdEZvckF0dHJpYnV0ZVJlcXVlc3REdG8SFgoOY29ycmVsYXRpb25faWQY", + "ASABKAkSHAoUaW5zdGFuY2VfdW5pcXVlX25hbWUYAiABKAkSFgoOYXR0cmli", + "dXRlX25hbWUYAyABKAkSOgoUdGFyZ2V0X3ZhbHVlX2VuY29kZWQYBCABKAsy", + "HC5nb29nbGUucHJvdG9idWYuU3RyaW5nVmFsdWUSKgoHdGltZW91dBgFIAEo", + "CzIZLmdvb2dsZS5wcm90b2J1Zi5EdXJhdGlvbhItCgl0aW1lc3RhbXAYBiAB", "KAsyGi5nb29nbGUucHJvdG9idWYuVGltZXN0YW1wEhsKE3BhcmVudF9leGVj", - "dXRpb25faWQYBSABKAkiywEKH1JvdXRlVG9HZXRBdHRyaWJ1dGVzUmVzcG9u", - "c2VEdG8SFgoOY29ycmVsYXRpb25faWQYASABKAkSOQoGdmFsdWVzGAIgASgL", - "Mikuc2NhZGFicmlkZ2Uuc2l0ZWNvbW1hbmQudjEuTG9vc2VWYWx1ZU1hcBIP", - "CgdzdWNjZXNzGAMgASgIEhUKDWVycm9yX21lc3NhZ2UYBCABKAkSLQoJdGlt", - "ZXN0YW1wGAUgASgLMhouZ29vZ2xlLnByb3RvYnVmLlRpbWVzdGFtcCLUAgoe", - "Um91dGVUb1NldEF0dHJpYnV0ZXNSZXF1ZXN0RHRvEhYKDmNvcnJlbGF0aW9u", - "X2lkGAEgASgJEhwKFGluc3RhbmNlX3VuaXF1ZV9uYW1lGAIgASgJEmkKEGF0", - "dHJpYnV0ZV92YWx1ZXMYAyADKAsyTy5zY2FkYWJyaWRnZS5zaXRlY29tbWFu", - "ZC52MS5Sb3V0ZVRvU2V0QXR0cmlidXRlc1JlcXVlc3REdG8uQXR0cmlidXRl", - "VmFsdWVzRW50cnkSLQoJdGltZXN0YW1wGAQgASgLMhouZ29vZ2xlLnByb3Rv", - "YnVmLlRpbWVzdGFtcBIbChNwYXJlbnRfZXhlY3V0aW9uX2lkGAUgASgJEg0K", - "BXRyYWNlGAYgASgIGjYKFEF0dHJpYnV0ZVZhbHVlc0VudHJ5EgsKA2tleRgB", - "IAEoCRINCgV2YWx1ZRgCIAEoCToCOAEikAEKH1JvdXRlVG9TZXRBdHRyaWJ1", - "dGVzUmVzcG9uc2VEdG8SFgoOY29ycmVsYXRpb25faWQYASABKAkSDwoHc3Vj", - "Y2VzcxgCIAEoCBIVCg1lcnJvcl9tZXNzYWdlGAMgASgJEi0KCXRpbWVzdGFt", - "cBgEIAEoCzIaLmdvb2dsZS5wcm90b2J1Zi5UaW1lc3RhbXAiwwIKIVJvdXRl", - "VG9XYWl0Rm9yQXR0cmlidXRlUmVxdWVzdER0bxIWCg5jb3JyZWxhdGlvbl9p", - "ZBgBIAEoCRIcChRpbnN0YW5jZV91bmlxdWVfbmFtZRgCIAEoCRIWCg5hdHRy", - "aWJ1dGVfbmFtZRgDIAEoCRI6ChR0YXJnZXRfdmFsdWVfZW5jb2RlZBgEIAEo", - "CzIcLmdvb2dsZS5wcm90b2J1Zi5TdHJpbmdWYWx1ZRIqCgd0aW1lb3V0GAUg", - "ASgLMhkuZ29vZ2xlLnByb3RvYnVmLkR1cmF0aW9uEi0KCXRpbWVzdGFtcBgG", - "IAEoCzIaLmdvb2dsZS5wcm90b2J1Zi5UaW1lc3RhbXASGwoTcGFyZW50X2V4", - "ZWN1dGlvbl9pZBgHIAEoCRIcChRyZXF1aXJlX2dvb2RfcXVhbGl0eRgIIAEo", - "CCL/AQoiUm91dGVUb1dhaXRGb3JBdHRyaWJ1dGVSZXNwb25zZUR0bxIWCg5j", - "b3JyZWxhdGlvbl9pZBgBIAEoCRIPCgdtYXRjaGVkGAIgASgIEjUKBXZhbHVl", - "GAMgASgLMiYuc2NhZGFicmlkZ2Uuc2l0ZWNvbW1hbmQudjEuTG9vc2VWYWx1", - "ZRIPCgdxdWFsaXR5GAQgASgJEhEKCXRpbWVkX291dBgFIAEoCBIPCgdzdWNj", - "ZXNzGAYgASgIEhUKDWVycm9yX21lc3NhZ2UYByABKAkSLQoJdGltZXN0YW1w", - "GAggASgLMhouZ29vZ2xlLnByb3RvYnVmLlRpbWVzdGFtcCKJAwoMUm91dGVS", - "ZXF1ZXN0EkoKDXJvdXRlX3RvX2NhbGwYASABKAsyMS5zY2FkYWJyaWRnZS5z", - "aXRlY29tbWFuZC52MS5Sb3V0ZVRvQ2FsbFJlcXVlc3REdG9IABJdChdyb3V0", - "ZV90b19nZXRfYXR0cmlidXRlcxgCIAEoCzI6LnNjYWRhYnJpZGdlLnNpdGVj", - "b21tYW5kLnYxLlJvdXRlVG9HZXRBdHRyaWJ1dGVzUmVxdWVzdER0b0gAEl0K", - "F3JvdXRlX3RvX3NldF9hdHRyaWJ1dGVzGAMgASgLMjouc2NhZGFicmlkZ2Uu", - "c2l0ZWNvbW1hbmQudjEuUm91dGVUb1NldEF0dHJpYnV0ZXNSZXF1ZXN0RHRv", - "SAASZAobcm91dGVfdG9fd2FpdF9mb3JfYXR0cmlidXRlGAQgASgLMj0uc2Nh", - "ZGFicmlkZ2Uuc2l0ZWNvbW1hbmQudjEuUm91dGVUb1dhaXRGb3JBdHRyaWJ1", - "dGVSZXF1ZXN0RHRvSABCCQoHY29tbWFuZCKJAwoKUm91dGVSZXBseRJLCg1y", - "b3V0ZV90b19jYWxsGAEgASgLMjIuc2NhZGFicmlkZ2Uuc2l0ZWNvbW1hbmQu", - "djEuUm91dGVUb0NhbGxSZXNwb25zZUR0b0gAEl4KF3JvdXRlX3RvX2dldF9h", - "dHRyaWJ1dGVzGAIgASgLMjsuc2NhZGFicmlkZ2Uuc2l0ZWNvbW1hbmQudjEu", - "Um91dGVUb0dldEF0dHJpYnV0ZXNSZXNwb25zZUR0b0gAEl4KF3JvdXRlX3Rv", - "X3NldF9hdHRyaWJ1dGVzGAMgASgLMjsuc2NhZGFicmlkZ2Uuc2l0ZWNvbW1h", - "bmQudjEuUm91dGVUb1NldEF0dHJpYnV0ZXNSZXNwb25zZUR0b0gAEmUKG3Jv", - "dXRlX3RvX3dhaXRfZm9yX2F0dHJpYnV0ZRgEIAEoCzI+LnNjYWRhYnJpZGdl", - "LnNpdGVjb21tYW5kLnYxLlJvdXRlVG9XYWl0Rm9yQXR0cmlidXRlUmVzcG9u", - "c2VEdG9IAEIHCgVyZXBseSJBChZUcmlnZ2VyU2l0ZUZhaWxvdmVyRHRvEhYK", - "DmNvcnJlbGF0aW9uX2lkGAEgASgJEg8KB3NpdGVfaWQYAiABKAkibQoSU2l0", - "ZUZhaWxvdmVyQWNrRHRvEhYKDmNvcnJlbGF0aW9uX2lkGAEgASgJEhAKCGFj", - "Y2VwdGVkGAIgASgIEhYKDnRhcmdldF9hZGRyZXNzGAMgASgJEhUKDWVycm9y", - "X21lc3NhZ2UYBCABKAkqywEKE0RlcGxveW1lbnRTdGF0dXNEdG8SJQohREVQ", - "TE9ZTUVOVF9TVEFUVVNfRFRPX1VOU1BFQ0lGSUVEEAASIQodREVQTE9ZTUVO", - "VF9TVEFUVVNfRFRPX1BFTkRJTkcQARIlCiFERVBMT1lNRU5UX1NUQVRVU19E", - "VE9fSU5fUFJPR1JFU1MQAhIhCh1ERVBMT1lNRU5UX1NUQVRVU19EVE9fU1VD", - "Q0VTUxADEiAKHERFUExPWU1FTlRfU1RBVFVTX0RUT19GQUlMRUQQBCrEAQoS", - "QnJvd3NlTm9kZUNsYXNzRHRvEiUKIUJST1dTRV9OT0RFX0NMQVNTX0RUT19V", - "TlNQRUNJRklFRBAAEiAKHEJST1dTRV9OT0RFX0NMQVNTX0RUT19PQkpFQ1QQ", - "ARIiCh5CUk9XU0VfTk9ERV9DTEFTU19EVE9fVkFSSUFCTEUQAhIgChxCUk9X", - "U0VfTk9ERV9DTEFTU19EVE9fTUVUSE9EEAMSHwobQlJPV1NFX05PREVfQ0xB", - "U1NfRFRPX09USEVSEAQqoQIKFEJyb3dzZUZhaWx1cmVLaW5kRHRvEicKI0JS", - "T1dTRV9GQUlMVVJFX0tJTkRfRFRPX1VOU1BFQ0lGSUVEEAASMAosQlJPV1NF", - "X0ZBSUxVUkVfS0lORF9EVE9fQ09OTkVDVElPTl9OT1RfRk9VTkQQARI0CjBC", - "Uk9XU0VfRkFJTFVSRV9LSU5EX0RUT19DT05ORUNUSU9OX05PVF9DT05ORUNU", - "RUQQAhIpCiVCUk9XU0VfRkFJTFVSRV9LSU5EX0RUT19OT1RfQlJPV1NBQkxF", - "EAMSIwofQlJPV1NFX0ZBSUxVUkVfS0lORF9EVE9fVElNRU9VVBAEEigKJEJS", - "T1dTRV9GQUlMVVJFX0tJTkRfRFRPX1NFUlZFUl9FUlJPUhAFKqoCChtSZWFk", - "VGFnVmFsdWVzRmFpbHVyZUtpbmREdG8SMAosUkVBRF9UQUdfVkFMVUVTX0ZB", - "SUxVUkVfS0lORF9EVE9fVU5TUEVDSUZJRUQQABI5CjVSRUFEX1RBR19WQUxV", - "RVNfRkFJTFVSRV9LSU5EX0RUT19DT05ORUNUSU9OX05PVF9GT1VORBABEj0K", - "OVJFQURfVEFHX1ZBTFVFU19GQUlMVVJFX0tJTkRfRFRPX0NPTk5FQ1RJT05f", - "Tk9UX0NPTk5FQ1RFRBACEiwKKFJFQURfVEFHX1ZBTFVFU19GQUlMVVJFX0tJ", - "TkRfRFRPX1RJTUVPVVQQAxIxCi1SRUFEX1RBR19WQUxVRVNfRkFJTFVSRV9L", - "SU5EX0RUT19TRVJWRVJfRVJST1IQBCqTAgoUVmVyaWZ5RmFpbHVyZUtpbmRE", - "dG8SJwojVkVSSUZZX0ZBSUxVUkVfS0lORF9EVE9fVU5TUEVDSUZJRUQQABIn", - "CiNWRVJJRllfRkFJTFVSRV9LSU5EX0RUT19VTlJFQUNIQUJMRRABEicKI1ZF", - "UklGWV9GQUlMVVJFX0tJTkRfRFRPX0FVVEhfRkFJTEVEEAISMQotVkVSSUZZ", - "X0ZBSUxVUkVfS0lORF9EVE9fVU5UUlVTVEVEX0NFUlRJRklDQVRFEAMSIwof", - "VkVSSUZZX0ZBSUxVUkVfS0lORF9EVE9fVElNRU9VVBAEEigKJFZFUklGWV9G", - "QUlMVVJFX0tJTkRfRFRPX1NFUlZFUl9FUlJPUhAFKuUBChpTdG9yZUFuZEZv", - "cndhcmRDYXRlZ29yeUR0bxIuCipTVE9SRV9BTkRfRk9SV0FSRF9DQVRFR09S", - "WV9EVE9fVU5TUEVDSUZJRUQQABIyCi5TVE9SRV9BTkRfRk9SV0FSRF9DQVRF", - "R09SWV9EVE9fRVhURVJOQUxfU1lTVEVNEAESLworU1RPUkVfQU5EX0ZPUldB", - "UkRfQ0FURUdPUllfRFRPX05PVElGSUNBVElPThACEjIKLlNUT1JFX0FORF9G", - "T1JXQVJEX0NBVEVHT1JZX0RUT19DQUNIRURfREJfV1JJVEUQAypoCg1BbGFy", - "bVN0YXRlRHRvEh8KG0FMQVJNX1NUQVRFX0RUT19VTlNQRUNJRklFRBAAEhoK", - "FkFMQVJNX1NUQVRFX0RUT19BQ1RJVkUQARIaChZBTEFSTV9TVEFURV9EVE9f", - "Tk9STUFMEAIquQEKDUFsYXJtTGV2ZWxEdG8SHwobQUxBUk1fTEVWRUxfRFRP", - "X1VOU1BFQ0lGSUVEEAASGAoUQUxBUk1fTEVWRUxfRFRPX05PTkUQARIXChNB", - "TEFSTV9MRVZFTF9EVE9fTE9XEAISGwoXQUxBUk1fTEVWRUxfRFRPX0xPV19M", - "T1cQAxIYChRBTEFSTV9MRVZFTF9EVE9fSElHSBAEEh0KGUFMQVJNX0xFVkVM", - "X0RUT19ISUdIX0hJR0gQBSqSAQoMQWxhcm1LaW5kRHRvEh4KGkFMQVJNX0tJ", - "TkRfRFRPX1VOU1BFQ0lGSUVEEAASGwoXQUxBUk1fS0lORF9EVE9fQ09NUFVU", - "RUQQARIgChxBTEFSTV9LSU5EX0RUT19OQVRJVkVfT1BDX1VBEAISIwofQUxB", - "Uk1fS0lORF9EVE9fTkFUSVZFX01YX0FDQ0VTUxADKugBChNBbGFybVNoZWx2", - "ZVN0YXRlRHRvEiYKIkFMQVJNX1NIRUxWRV9TVEFURV9EVE9fVU5TUEVDSUZJ", - "RUQQABIkCiBBTEFSTV9TSEVMVkVfU1RBVEVfRFRPX1VOU0hFTFZFRBABEisK", - "J0FMQVJNX1NIRUxWRV9TVEFURV9EVE9fT05FX1NIT1RfU0hFTFZFRBACEigK", - "JEFMQVJNX1NIRUxWRV9TVEFURV9EVE9fVElNRURfU0hFTFZFRBADEiwKKEFM", - "QVJNX1NIRUxWRV9TVEFURV9EVE9fUEVSTUFORU5UX1NIRUxWRUQQBDKEBQoS", - "U2l0ZUNvbW1hbmRTZXJ2aWNlEmwKEEV4ZWN1dGVMaWZlY3ljbGUSLC5zY2Fk", - "YWJyaWRnZS5zaXRlY29tbWFuZC52MS5MaWZlY3ljbGVSZXF1ZXN0Giouc2Nh", - "ZGFicmlkZ2Uuc2l0ZWNvbW1hbmQudjEuTGlmZWN5Y2xlUmVwbHkSYAoMRXhl", - "Y3V0ZU9wY1VhEiguc2NhZGFicmlkZ2Uuc2l0ZWNvbW1hbmQudjEuT3BjVWFS", - "ZXF1ZXN0GiYuc2NhZGFicmlkZ2Uuc2l0ZWNvbW1hbmQudjEuT3BjVWFSZXBs", - "eRJgCgxFeGVjdXRlUXVlcnkSKC5zY2FkYWJyaWRnZS5zaXRlY29tbWFuZC52", - "MS5RdWVyeVJlcXVlc3QaJi5zY2FkYWJyaWRnZS5zaXRlY29tbWFuZC52MS5R", - "dWVyeVJlcGx5EmMKDUV4ZWN1dGVQYXJrZWQSKS5zY2FkYWJyaWRnZS5zaXRl", - "Y29tbWFuZC52MS5QYXJrZWRSZXF1ZXN0Gicuc2NhZGFicmlkZ2Uuc2l0ZWNv", - "bW1hbmQudjEuUGFya2VkUmVwbHkSYAoMRXhlY3V0ZVJvdXRlEiguc2NhZGFi", - "cmlkZ2Uuc2l0ZWNvbW1hbmQudjEuUm91dGVSZXF1ZXN0GiYuc2NhZGFicmlk", - "Z2Uuc2l0ZWNvbW1hbmQudjEuUm91dGVSZXBseRJ1Cg9UcmlnZ2VyRmFpbG92", - "ZXISMi5zY2FkYWJyaWRnZS5zaXRlY29tbWFuZC52MS5UcmlnZ2VyU2l0ZUZh", - "aWxvdmVyRHRvGi4uc2NhZGFicmlkZ2Uuc2l0ZWNvbW1hbmQudjEuU2l0ZUZh", - "aWxvdmVyQWNrRHRvQiuqAihaQi5NT00uV1cuU2NhZGFCcmlkZ2UuQ29tbXVu", - "aWNhdGlvbi5HcnBjYgZwcm90bzM=")); + "dXRpb25faWQYByABKAkSHAoUcmVxdWlyZV9nb29kX3F1YWxpdHkYCCABKAgi", + "/wEKIlJvdXRlVG9XYWl0Rm9yQXR0cmlidXRlUmVzcG9uc2VEdG8SFgoOY29y", + "cmVsYXRpb25faWQYASABKAkSDwoHbWF0Y2hlZBgCIAEoCBI1CgV2YWx1ZRgD", + "IAEoCzImLnNjYWRhYnJpZGdlLnNpdGVjb21tYW5kLnYxLkxvb3NlVmFsdWUS", + "DwoHcXVhbGl0eRgEIAEoCRIRCgl0aW1lZF9vdXQYBSABKAgSDwoHc3VjY2Vz", + "cxgGIAEoCBIVCg1lcnJvcl9tZXNzYWdlGAcgASgJEi0KCXRpbWVzdGFtcBgI", + "IAEoCzIaLmdvb2dsZS5wcm90b2J1Zi5UaW1lc3RhbXAiiQMKDFJvdXRlUmVx", + "dWVzdBJKCg1yb3V0ZV90b19jYWxsGAEgASgLMjEuc2NhZGFicmlkZ2Uuc2l0", + "ZWNvbW1hbmQudjEuUm91dGVUb0NhbGxSZXF1ZXN0RHRvSAASXQoXcm91dGVf", + "dG9fZ2V0X2F0dHJpYnV0ZXMYAiABKAsyOi5zY2FkYWJyaWRnZS5zaXRlY29t", + "bWFuZC52MS5Sb3V0ZVRvR2V0QXR0cmlidXRlc1JlcXVlc3REdG9IABJdChdy", + "b3V0ZV90b19zZXRfYXR0cmlidXRlcxgDIAEoCzI6LnNjYWRhYnJpZGdlLnNp", + "dGVjb21tYW5kLnYxLlJvdXRlVG9TZXRBdHRyaWJ1dGVzUmVxdWVzdER0b0gA", + "EmQKG3JvdXRlX3RvX3dhaXRfZm9yX2F0dHJpYnV0ZRgEIAEoCzI9LnNjYWRh", + "YnJpZGdlLnNpdGVjb21tYW5kLnYxLlJvdXRlVG9XYWl0Rm9yQXR0cmlidXRl", + "UmVxdWVzdER0b0gAQgkKB2NvbW1hbmQiiQMKClJvdXRlUmVwbHkSSwoNcm91", + "dGVfdG9fY2FsbBgBIAEoCzIyLnNjYWRhYnJpZGdlLnNpdGVjb21tYW5kLnYx", + "LlJvdXRlVG9DYWxsUmVzcG9uc2VEdG9IABJeChdyb3V0ZV90b19nZXRfYXR0", + "cmlidXRlcxgCIAEoCzI7LnNjYWRhYnJpZGdlLnNpdGVjb21tYW5kLnYxLlJv", + "dXRlVG9HZXRBdHRyaWJ1dGVzUmVzcG9uc2VEdG9IABJeChdyb3V0ZV90b19z", + "ZXRfYXR0cmlidXRlcxgDIAEoCzI7LnNjYWRhYnJpZGdlLnNpdGVjb21tYW5k", + "LnYxLlJvdXRlVG9TZXRBdHRyaWJ1dGVzUmVzcG9uc2VEdG9IABJlChtyb3V0", + "ZV90b193YWl0X2Zvcl9hdHRyaWJ1dGUYBCABKAsyPi5zY2FkYWJyaWRnZS5z", + "aXRlY29tbWFuZC52MS5Sb3V0ZVRvV2FpdEZvckF0dHJpYnV0ZVJlc3BvbnNl", + "RHRvSABCBwoFcmVwbHkiQQoWVHJpZ2dlclNpdGVGYWlsb3ZlckR0bxIWCg5j", + "b3JyZWxhdGlvbl9pZBgBIAEoCRIPCgdzaXRlX2lkGAIgASgJIm0KElNpdGVG", + "YWlsb3ZlckFja0R0bxIWCg5jb3JyZWxhdGlvbl9pZBgBIAEoCRIQCghhY2Nl", + "cHRlZBgCIAEoCBIWCg50YXJnZXRfYWRkcmVzcxgDIAEoCRIVCg1lcnJvcl9t", + "ZXNzYWdlGAQgASgJKssBChNEZXBsb3ltZW50U3RhdHVzRHRvEiUKIURFUExP", + "WU1FTlRfU1RBVFVTX0RUT19VTlNQRUNJRklFRBAAEiEKHURFUExPWU1FTlRf", + "U1RBVFVTX0RUT19QRU5ESU5HEAESJQohREVQTE9ZTUVOVF9TVEFUVVNfRFRP", + "X0lOX1BST0dSRVNTEAISIQodREVQTE9ZTUVOVF9TVEFUVVNfRFRPX1NVQ0NF", + "U1MQAxIgChxERVBMT1lNRU5UX1NUQVRVU19EVE9fRkFJTEVEEAQqxAEKEkJy", + "b3dzZU5vZGVDbGFzc0R0bxIlCiFCUk9XU0VfTk9ERV9DTEFTU19EVE9fVU5T", + "UEVDSUZJRUQQABIgChxCUk9XU0VfTk9ERV9DTEFTU19EVE9fT0JKRUNUEAES", + "IgoeQlJPV1NFX05PREVfQ0xBU1NfRFRPX1ZBUklBQkxFEAISIAocQlJPV1NF", + "X05PREVfQ0xBU1NfRFRPX01FVEhPRBADEh8KG0JST1dTRV9OT0RFX0NMQVNT", + "X0RUT19PVEhFUhAEKqECChRCcm93c2VGYWlsdXJlS2luZER0bxInCiNCUk9X", + "U0VfRkFJTFVSRV9LSU5EX0RUT19VTlNQRUNJRklFRBAAEjAKLEJST1dTRV9G", + "QUlMVVJFX0tJTkRfRFRPX0NPTk5FQ1RJT05fTk9UX0ZPVU5EEAESNAowQlJP", + "V1NFX0ZBSUxVUkVfS0lORF9EVE9fQ09OTkVDVElPTl9OT1RfQ09OTkVDVEVE", + "EAISKQolQlJPV1NFX0ZBSUxVUkVfS0lORF9EVE9fTk9UX0JST1dTQUJMRRAD", + "EiMKH0JST1dTRV9GQUlMVVJFX0tJTkRfRFRPX1RJTUVPVVQQBBIoCiRCUk9X", + "U0VfRkFJTFVSRV9LSU5EX0RUT19TRVJWRVJfRVJST1IQBSqqAgobUmVhZFRh", + "Z1ZhbHVlc0ZhaWx1cmVLaW5kRHRvEjAKLFJFQURfVEFHX1ZBTFVFU19GQUlM", + "VVJFX0tJTkRfRFRPX1VOU1BFQ0lGSUVEEAASOQo1UkVBRF9UQUdfVkFMVUVT", + "X0ZBSUxVUkVfS0lORF9EVE9fQ09OTkVDVElPTl9OT1RfRk9VTkQQARI9CjlS", + "RUFEX1RBR19WQUxVRVNfRkFJTFVSRV9LSU5EX0RUT19DT05ORUNUSU9OX05P", + "VF9DT05ORUNURUQQAhIsCihSRUFEX1RBR19WQUxVRVNfRkFJTFVSRV9LSU5E", + "X0RUT19USU1FT1VUEAMSMQotUkVBRF9UQUdfVkFMVUVTX0ZBSUxVUkVfS0lO", + "RF9EVE9fU0VSVkVSX0VSUk9SEAQqkwIKFFZlcmlmeUZhaWx1cmVLaW5kRHRv", + "EicKI1ZFUklGWV9GQUlMVVJFX0tJTkRfRFRPX1VOU1BFQ0lGSUVEEAASJwoj", + "VkVSSUZZX0ZBSUxVUkVfS0lORF9EVE9fVU5SRUFDSEFCTEUQARInCiNWRVJJ", + "RllfRkFJTFVSRV9LSU5EX0RUT19BVVRIX0ZBSUxFRBACEjEKLVZFUklGWV9G", + "QUlMVVJFX0tJTkRfRFRPX1VOVFJVU1RFRF9DRVJUSUZJQ0FURRADEiMKH1ZF", + "UklGWV9GQUlMVVJFX0tJTkRfRFRPX1RJTUVPVVQQBBIoCiRWRVJJRllfRkFJ", + "TFVSRV9LSU5EX0RUT19TRVJWRVJfRVJST1IQBSrlAQoaU3RvcmVBbmRGb3J3", + "YXJkQ2F0ZWdvcnlEdG8SLgoqU1RPUkVfQU5EX0ZPUldBUkRfQ0FURUdPUllf", + "RFRPX1VOU1BFQ0lGSUVEEAASMgouU1RPUkVfQU5EX0ZPUldBUkRfQ0FURUdP", + "UllfRFRPX0VYVEVSTkFMX1NZU1RFTRABEi8KK1NUT1JFX0FORF9GT1JXQVJE", + "X0NBVEVHT1JZX0RUT19OT1RJRklDQVRJT04QAhIyCi5TVE9SRV9BTkRfRk9S", + "V0FSRF9DQVRFR09SWV9EVE9fQ0FDSEVEX0RCX1dSSVRFEAMqaAoNQWxhcm1T", + "dGF0ZUR0bxIfChtBTEFSTV9TVEFURV9EVE9fVU5TUEVDSUZJRUQQABIaChZB", + "TEFSTV9TVEFURV9EVE9fQUNUSVZFEAESGgoWQUxBUk1fU1RBVEVfRFRPX05P", + "Uk1BTBACKrkBCg1BbGFybUxldmVsRHRvEh8KG0FMQVJNX0xFVkVMX0RUT19V", + "TlNQRUNJRklFRBAAEhgKFEFMQVJNX0xFVkVMX0RUT19OT05FEAESFwoTQUxB", + "Uk1fTEVWRUxfRFRPX0xPVxACEhsKF0FMQVJNX0xFVkVMX0RUT19MT1dfTE9X", + "EAMSGAoUQUxBUk1fTEVWRUxfRFRPX0hJR0gQBBIdChlBTEFSTV9MRVZFTF9E", + "VE9fSElHSF9ISUdIEAUqkgEKDEFsYXJtS2luZER0bxIeChpBTEFSTV9LSU5E", + "X0RUT19VTlNQRUNJRklFRBAAEhsKF0FMQVJNX0tJTkRfRFRPX0NPTVBVVEVE", + "EAESIAocQUxBUk1fS0lORF9EVE9fTkFUSVZFX09QQ19VQRACEiMKH0FMQVJN", + "X0tJTkRfRFRPX05BVElWRV9NWF9BQ0NFU1MQAyroAQoTQWxhcm1TaGVsdmVT", + "dGF0ZUR0bxImCiJBTEFSTV9TSEVMVkVfU1RBVEVfRFRPX1VOU1BFQ0lGSUVE", + "EAASJAogQUxBUk1fU0hFTFZFX1NUQVRFX0RUT19VTlNIRUxWRUQQARIrCidB", + "TEFSTV9TSEVMVkVfU1RBVEVfRFRPX09ORV9TSE9UX1NIRUxWRUQQAhIoCiRB", + "TEFSTV9TSEVMVkVfU1RBVEVfRFRPX1RJTUVEX1NIRUxWRUQQAxIsCihBTEFS", + "TV9TSEVMVkVfU1RBVEVfRFRPX1BFUk1BTkVOVF9TSEVMVkVEEAQyhAUKElNp", + "dGVDb21tYW5kU2VydmljZRJsChBFeGVjdXRlTGlmZWN5Y2xlEiwuc2NhZGFi", + "cmlkZ2Uuc2l0ZWNvbW1hbmQudjEuTGlmZWN5Y2xlUmVxdWVzdBoqLnNjYWRh", + "YnJpZGdlLnNpdGVjb21tYW5kLnYxLkxpZmVjeWNsZVJlcGx5EmAKDEV4ZWN1", + "dGVPcGNVYRIoLnNjYWRhYnJpZGdlLnNpdGVjb21tYW5kLnYxLk9wY1VhUmVx", + "dWVzdBomLnNjYWRhYnJpZGdlLnNpdGVjb21tYW5kLnYxLk9wY1VhUmVwbHkS", + "YAoMRXhlY3V0ZVF1ZXJ5Eiguc2NhZGFicmlkZ2Uuc2l0ZWNvbW1hbmQudjEu", + "UXVlcnlSZXF1ZXN0GiYuc2NhZGFicmlkZ2Uuc2l0ZWNvbW1hbmQudjEuUXVl", + "cnlSZXBseRJjCg1FeGVjdXRlUGFya2VkEikuc2NhZGFicmlkZ2Uuc2l0ZWNv", + "bW1hbmQudjEuUGFya2VkUmVxdWVzdBonLnNjYWRhYnJpZGdlLnNpdGVjb21t", + "YW5kLnYxLlBhcmtlZFJlcGx5EmAKDEV4ZWN1dGVSb3V0ZRIoLnNjYWRhYnJp", + "ZGdlLnNpdGVjb21tYW5kLnYxLlJvdXRlUmVxdWVzdBomLnNjYWRhYnJpZGdl", + "LnNpdGVjb21tYW5kLnYxLlJvdXRlUmVwbHkSdQoPVHJpZ2dlckZhaWxvdmVy", + "EjIuc2NhZGFicmlkZ2Uuc2l0ZWNvbW1hbmQudjEuVHJpZ2dlclNpdGVGYWls", + "b3ZlckR0bxouLnNjYWRhYnJpZGdlLnNpdGVjb21tYW5kLnYxLlNpdGVGYWls", + "b3ZlckFja0R0b0IrqgIoWkIuTU9NLldXLlNjYWRhQnJpZGdlLkNvbW11bmlj", + "YXRpb24uR3JwY2IGcHJvdG8z")); descriptor = pbr::FileDescriptor.FromGeneratedCode(descriptorData, new pbr::FileDescriptor[] { global::Google.Protobuf.WellKnownTypes.TimestampReflection.Descriptor, global::Google.Protobuf.WellKnownTypes.DurationReflection.Descriptor, global::Google.Protobuf.WellKnownTypes.WrappersReflection.Descriptor, global::ZB.MOM.WW.ScadaBridge.Communication.Grpc.SitestreamReflection.Descriptor, }, new pbr::GeneratedClrTypeInfo(new[] {typeof(global::ZB.MOM.WW.ScadaBridge.Communication.Grpc.DeploymentStatusDto), typeof(global::ZB.MOM.WW.ScadaBridge.Communication.Grpc.BrowseNodeClassDto), typeof(global::ZB.MOM.WW.ScadaBridge.Communication.Grpc.BrowseFailureKindDto), typeof(global::ZB.MOM.WW.ScadaBridge.Communication.Grpc.ReadTagValuesFailureKindDto), typeof(global::ZB.MOM.WW.ScadaBridge.Communication.Grpc.VerifyFailureKindDto), typeof(global::ZB.MOM.WW.ScadaBridge.Communication.Grpc.StoreAndForwardCategoryDto), typeof(global::ZB.MOM.WW.ScadaBridge.Communication.Grpc.AlarmStateDto), typeof(global::ZB.MOM.WW.ScadaBridge.Communication.Grpc.AlarmLevelDto), typeof(global::ZB.MOM.WW.ScadaBridge.Communication.Grpc.AlarmKindDto), typeof(global::ZB.MOM.WW.ScadaBridge.Communication.Grpc.AlarmShelveStateDto), }, null, new pbr::GeneratedClrTypeInfo[] { @@ -598,7 +599,7 @@ namespace ZB.MOM.WW.ScadaBridge.Communication.Grpc { new pbr::GeneratedClrTypeInfo(typeof(global::ZB.MOM.WW.ScadaBridge.Communication.Grpc.ParkedRequest), global::ZB.MOM.WW.ScadaBridge.Communication.Grpc.ParkedRequest.Parser, new[]{ "ParkedMessageQuery", "ParkedMessageRetry", "ParkedMessageDiscard", "RetryParkedOperation", "DiscardParkedOperation" }, new[]{ "Command" }, null, null, null), new pbr::GeneratedClrTypeInfo(typeof(global::ZB.MOM.WW.ScadaBridge.Communication.Grpc.ParkedReply), global::ZB.MOM.WW.ScadaBridge.Communication.Grpc.ParkedReply.Parser, new[]{ "ParkedMessageQuery", "ParkedMessageRetry", "ParkedMessageDiscard", "ParkedOperationAction" }, new[]{ "Reply" }, null, null, null), new pbr::GeneratedClrTypeInfo(typeof(global::ZB.MOM.WW.ScadaBridge.Communication.Grpc.RouteToCallRequestDto), global::ZB.MOM.WW.ScadaBridge.Communication.Grpc.RouteToCallRequestDto.Parser, new[]{ "CorrelationId", "InstanceUniqueName", "ScriptName", "Parameters", "Timestamp", "ParentExecutionId", "Trace" }, null, null, null, null), - new pbr::GeneratedClrTypeInfo(typeof(global::ZB.MOM.WW.ScadaBridge.Communication.Grpc.RouteToCallResponseDto), global::ZB.MOM.WW.ScadaBridge.Communication.Grpc.RouteToCallResponseDto.Parser, new[]{ "CorrelationId", "Success", "ReturnValue", "ErrorMessage", "Timestamp", "ChildExecutionId", "TraceEvents" }, null, null, null, null), + new pbr::GeneratedClrTypeInfo(typeof(global::ZB.MOM.WW.ScadaBridge.Communication.Grpc.RouteToCallResponseDto), global::ZB.MOM.WW.ScadaBridge.Communication.Grpc.RouteToCallResponseDto.Parser, new[]{ "CorrelationId", "Success", "ReturnValue", "ErrorMessage", "Timestamp", "ChildExecutionId", "TraceEvents", "Truncated", "LogsTruncated" }, null, null, null, null), new pbr::GeneratedClrTypeInfo(typeof(global::ZB.MOM.WW.ScadaBridge.Communication.Grpc.RouteToGetAttributesRequestDto), global::ZB.MOM.WW.ScadaBridge.Communication.Grpc.RouteToGetAttributesRequestDto.Parser, new[]{ "CorrelationId", "InstanceUniqueName", "AttributeNames", "Timestamp", "ParentExecutionId" }, null, null, null, null), new pbr::GeneratedClrTypeInfo(typeof(global::ZB.MOM.WW.ScadaBridge.Communication.Grpc.RouteToGetAttributesResponseDto), global::ZB.MOM.WW.ScadaBridge.Communication.Grpc.RouteToGetAttributesResponseDto.Parser, new[]{ "CorrelationId", "Values", "Success", "ErrorMessage", "Timestamp" }, null, null, null, null), new pbr::GeneratedClrTypeInfo(typeof(global::ZB.MOM.WW.ScadaBridge.Communication.Grpc.RouteToSetAttributesRequestDto), global::ZB.MOM.WW.ScadaBridge.Communication.Grpc.RouteToSetAttributesRequestDto.Parser, new[]{ "CorrelationId", "InstanceUniqueName", "AttributeValues", "Timestamp", "ParentExecutionId", "Trace" }, null, null, null, new pbr::GeneratedClrTypeInfo[] { null, }), @@ -28991,6 +28992,8 @@ namespace ZB.MOM.WW.ScadaBridge.Communication.Grpc { timestamp_ = other.timestamp_ != null ? other.timestamp_.Clone() : null; childExecutionId_ = other.childExecutionId_; traceEvents_ = other.traceEvents_.Clone(); + truncated_ = other.truncated_; + logsTruncated_ = other.logsTruncated_; _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields); } @@ -29097,6 +29100,34 @@ namespace ZB.MOM.WW.ScadaBridge.Communication.Grpc { get { return traceEvents_; } } + /// Field number for the "truncated" field. + public const int TruncatedFieldNumber = 8; + private bool truncated_; + /// + /// Flow Debugger (Gitea #54): the site's inline trace collector dropped step rows (truncated) + /// or log-line rows (logs_truncated). proto3 default false from an untraced run or older site. + /// + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public bool Truncated { + get { return truncated_; } + set { + truncated_ = value; + } + } + + /// Field number for the "logs_truncated" field. + public const int LogsTruncatedFieldNumber = 9; + private bool logsTruncated_; + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public bool LogsTruncated { + get { return logsTruncated_; } + set { + logsTruncated_ = value; + } + } + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] public override bool Equals(object other) { @@ -29119,6 +29150,8 @@ namespace ZB.MOM.WW.ScadaBridge.Communication.Grpc { if (!object.Equals(Timestamp, other.Timestamp)) return false; if (ChildExecutionId != other.ChildExecutionId) return false; if(!traceEvents_.Equals(other.traceEvents_)) return false; + if (Truncated != other.Truncated) return false; + if (LogsTruncated != other.LogsTruncated) return false; return Equals(_unknownFields, other._unknownFields); } @@ -29133,6 +29166,8 @@ namespace ZB.MOM.WW.ScadaBridge.Communication.Grpc { if (timestamp_ != null) hash ^= Timestamp.GetHashCode(); if (ChildExecutionId.Length != 0) hash ^= ChildExecutionId.GetHashCode(); hash ^= traceEvents_.GetHashCode(); + if (Truncated != false) hash ^= Truncated.GetHashCode(); + if (LogsTruncated != false) hash ^= LogsTruncated.GetHashCode(); if (_unknownFields != null) { hash ^= _unknownFields.GetHashCode(); } @@ -29176,6 +29211,14 @@ namespace ZB.MOM.WW.ScadaBridge.Communication.Grpc { output.WriteString(ChildExecutionId); } traceEvents_.WriteTo(output, _repeated_traceEvents_codec); + if (Truncated != false) { + output.WriteRawTag(64); + output.WriteBool(Truncated); + } + if (LogsTruncated != false) { + output.WriteRawTag(72); + output.WriteBool(LogsTruncated); + } if (_unknownFields != null) { _unknownFields.WriteTo(output); } @@ -29211,6 +29254,14 @@ namespace ZB.MOM.WW.ScadaBridge.Communication.Grpc { output.WriteString(ChildExecutionId); } traceEvents_.WriteTo(ref output, _repeated_traceEvents_codec); + if (Truncated != false) { + output.WriteRawTag(64); + output.WriteBool(Truncated); + } + if (LogsTruncated != false) { + output.WriteRawTag(72); + output.WriteBool(LogsTruncated); + } if (_unknownFields != null) { _unknownFields.WriteTo(ref output); } @@ -29240,6 +29291,12 @@ namespace ZB.MOM.WW.ScadaBridge.Communication.Grpc { size += 1 + pb::CodedOutputStream.ComputeStringSize(ChildExecutionId); } size += traceEvents_.CalculateSize(_repeated_traceEvents_codec); + if (Truncated != false) { + size += 1 + 1; + } + if (LogsTruncated != false) { + size += 1 + 1; + } if (_unknownFields != null) { size += _unknownFields.CalculateSize(); } @@ -29277,6 +29334,12 @@ namespace ZB.MOM.WW.ScadaBridge.Communication.Grpc { ChildExecutionId = other.ChildExecutionId; } traceEvents_.Add(other.traceEvents_); + if (other.Truncated != false) { + Truncated = other.Truncated; + } + if (other.LogsTruncated != false) { + LogsTruncated = other.LogsTruncated; + } _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields); } @@ -29330,6 +29393,14 @@ namespace ZB.MOM.WW.ScadaBridge.Communication.Grpc { traceEvents_.AddEntriesFrom(input, _repeated_traceEvents_codec); break; } + case 64: { + Truncated = input.ReadBool(); + break; + } + case 72: { + LogsTruncated = input.ReadBool(); + break; + } } } #endif @@ -29383,6 +29454,14 @@ namespace ZB.MOM.WW.ScadaBridge.Communication.Grpc { traceEvents_.AddEntriesFrom(ref input, _repeated_traceEvents_codec); break; } + case 64: { + Truncated = input.ReadBool(); + break; + } + case 72: { + LogsTruncated = input.ReadBool(); + break; + } } } } diff --git a/src/ZB.MOM.WW.ScadaBridge.InboundAPI/RouteHelper.cs b/src/ZB.MOM.WW.ScadaBridge.InboundAPI/RouteHelper.cs index 5ed42d5b..db80f34d 100644 --- a/src/ZB.MOM.WW.ScadaBridge.InboundAPI/RouteHelper.cs +++ b/src/ZB.MOM.WW.ScadaBridge.InboundAPI/RouteHelper.cs @@ -86,6 +86,7 @@ public class RouteHelper private readonly INodeIdentityProvider? _nodeIdentity; private readonly bool _trace; private readonly Action? _traceSink; + private readonly Action? _siteTruncationSink; private readonly IReadOnlySet? _permittedSiteIdentifiers; private readonly bool _captureBodies; private readonly ILogger? _logger; @@ -107,7 +108,7 @@ public class RouteHelper INodeIdentityProvider? nodeIdentity = null, ILogger? logger = null) : this(instanceLocator, instanceRouter, CancellationToken.None, CancellationToken.None, parentExecutionId: null, - auditWriter, nodeIdentity, trace: false, traceSink: null, permittedSiteIdentifiers: null, captureBodies: true, logger) + auditWriter, nodeIdentity, trace: false, traceSink: null, permittedSiteIdentifiers: null, captureBodies: true, logger, siteTruncationSink: null) { } @@ -123,7 +124,8 @@ public class RouteHelper Action? traceSink, IReadOnlySet? permittedSiteIdentifiers, bool captureBodies, - ILogger? logger) + ILogger? logger, + Action? siteTruncationSink) { _permittedSiteIdentifiers = permittedSiteIdentifiers; _instanceLocator = instanceLocator; @@ -135,6 +137,7 @@ public class RouteHelper _nodeIdentity = nodeIdentity; _trace = trace; _traceSink = traceSink; + _siteTruncationSink = siteTruncationSink; _captureBodies = captureBodies; _logger = logger; } @@ -150,7 +153,7 @@ public class RouteHelper /// A new inheriting the given deadline token. public RouteHelper WithDeadline(CancellationToken deadlineToken) => new(_instanceLocator, _instanceRouter, deadlineToken, _requestAbortedToken, _parentExecutionId, - _auditWriter, _nodeIdentity, _trace, _traceSink, _permittedSiteIdentifiers, _captureBodies, _logger); + _auditWriter, _nodeIdentity, _trace, _traceSink, _permittedSiteIdentifiers, _captureBodies, _logger, _siteTruncationSink); /// /// Returns a carrying the raw request-abort @@ -164,7 +167,7 @@ public class RouteHelper /// A new carrying the given request-abort token. public RouteHelper WithRequestAborted(CancellationToken requestAbortedToken) => new(_instanceLocator, _instanceRouter, _deadlineToken, requestAbortedToken, _parentExecutionId, - _auditWriter, _nodeIdentity, _trace, _traceSink, _permittedSiteIdentifiers, _captureBodies, _logger); + _auditWriter, _nodeIdentity, _trace, _traceSink, _permittedSiteIdentifiers, _captureBodies, _logger, _siteTruncationSink); /// /// Audit Log (ParentExecutionId): returns a whose @@ -179,7 +182,7 @@ public class RouteHelper /// A new carrying the given parent execution id. public RouteHelper WithParentExecutionId(Guid? parentExecutionId) => new(_instanceLocator, _instanceRouter, _deadlineToken, _requestAbortedToken, parentExecutionId, - _auditWriter, _nodeIdentity, _trace, _traceSink, _permittedSiteIdentifiers, _captureBodies, _logger); + _auditWriter, _nodeIdentity, _trace, _traceSink, _permittedSiteIdentifiers, _captureBodies, _logger, _siteTruncationSink); /// /// Flow Debugger (design 2026-09-05 §5.2): returns a whose routed @@ -190,7 +193,7 @@ public class RouteHelper /// A new carrying the flag. public RouteHelper WithTrace(bool trace) => new(_instanceLocator, _instanceRouter, _deadlineToken, _requestAbortedToken, _parentExecutionId, - _auditWriter, _nodeIdentity, trace, _traceSink, _permittedSiteIdentifiers, _captureBodies, _logger); + _auditWriter, _nodeIdentity, trace, _traceSink, _permittedSiteIdentifiers, _captureBodies, _logger, _siteTruncationSink); /// /// Flow Debugger (design 2026-09-05 §7.1): returns a that also @@ -199,10 +202,15 @@ public class RouteHelper /// audit store. Null clears the sink. /// /// Receives the audit events in emission order; null = none. + /// + /// Gitea #54: receives (truncated, logsTruncated) from a routed reply whose SITE-side + /// inline collector dropped rows, so the caller can label a short list as short. Called only + /// when at least one flag is set; null = none. + /// /// A new carrying the sink. - public RouteHelper WithTraceSink(Action? traceSink) => + public RouteHelper WithTraceSink(Action? traceSink, Action? siteTruncationSink = null) => new(_instanceLocator, _instanceRouter, _deadlineToken, _requestAbortedToken, _parentExecutionId, - _auditWriter, _nodeIdentity, _trace, traceSink, _permittedSiteIdentifiers, _captureBodies, _logger); + _auditWriter, _nodeIdentity, _trace, traceSink, _permittedSiteIdentifiers, _captureBodies, _logger, siteTruncationSink); /// /// Flow Debugger outbound calls (plan 2026-09-06, D5/D7): the trace sink this helper carries, @@ -236,7 +244,7 @@ public class RouteHelper /// A new carrying the scope. public RouteHelper WithSiteScope(IReadOnlySet? permittedSiteIdentifiers) => new(_instanceLocator, _instanceRouter, _deadlineToken, _requestAbortedToken, _parentExecutionId, - _auditWriter, _nodeIdentity, _trace, _traceSink, permittedSiteIdentifiers, _captureBodies, _logger); + _auditWriter, _nodeIdentity, _trace, _traceSink, permittedSiteIdentifiers, _captureBodies, _logger, _siteTruncationSink); /// /// Arch-review F11: returns a whose always-on RouteCall row @@ -251,7 +259,7 @@ public class RouteHelper /// A new carrying the flag. public RouteHelper WithBodyCapture(bool captureBodies) => new(_instanceLocator, _instanceRouter, _deadlineToken, _requestAbortedToken, _parentExecutionId, - _auditWriter, _nodeIdentity, _trace, _traceSink, _permittedSiteIdentifiers, captureBodies, _logger); + _auditWriter, _nodeIdentity, _trace, _traceSink, _permittedSiteIdentifiers, captureBodies, _logger, _siteTruncationSink); /// /// Creates a route target for the specified instance. @@ -262,7 +270,7 @@ public class RouteHelper { return new RouteTarget( instanceCode, _instanceLocator, _instanceRouter, _deadlineToken, _requestAbortedToken, _parentExecutionId, - _auditWriter, _nodeIdentity, _trace, _traceSink, _permittedSiteIdentifiers, _captureBodies, _logger); + _auditWriter, _nodeIdentity, _trace, _traceSink, _permittedSiteIdentifiers, _captureBodies, _logger, _siteTruncationSink); } } @@ -281,6 +289,7 @@ public class RouteTarget private readonly INodeIdentityProvider? _nodeIdentity; private readonly bool _trace; private readonly Action? _traceSink; + private readonly Action? _siteTruncationSink; private readonly IReadOnlySet? _permittedSiteIdentifiers; private readonly bool _captureBodies; private readonly ILogger? _logger; @@ -301,6 +310,7 @@ public class RouteTarget /// Arch-review F2: site identifiers this call may reach; null = unscoped. /// Arch-review F11: false omits the request/response summary on the RouteCall row. /// Arch-review F8: logs a Warning when the best-effort RouteCall audit write faults; null = silent. + /// Gitea #54: receives a routed reply's site-side truncation flags; null = none. internal RouteTarget( string instanceCode, IInstanceLocator instanceLocator, @@ -314,7 +324,8 @@ public class RouteTarget Action? traceSink = null, IReadOnlySet? permittedSiteIdentifiers = null, bool captureBodies = true, - ILogger? logger = null) + ILogger? logger = null, + Action? siteTruncationSink = null) { _permittedSiteIdentifiers = permittedSiteIdentifiers; _instanceCode = instanceCode; @@ -327,6 +338,7 @@ public class RouteTarget _nodeIdentity = nodeIdentity; _trace = trace; _traceSink = traceSink; + _siteTruncationSink = siteTruncationSink; _captureBodies = captureBodies; _logger = logger; } @@ -382,6 +394,7 @@ public class RouteTarget EmitRouteCall(scriptName, normalized, startedUtc, sw.ElapsedMilliseconds, response.Success, response.ReturnValue, response.ErrorMessage, response.ChildExecutionId, response.TraceEvents); + ReportSiteTruncation(response); if (!response.Success) { @@ -463,6 +476,23 @@ public class RouteTarget } } + /// + /// Gitea #54: forwards a routed reply's site-side truncation flags to the sink. Best-effort — + /// a misbehaving sink never fails the routed call. + /// + private void ReportSiteTruncation(RouteToCallResponse response) + { + if (_siteTruncationSink is null || !(response.Truncated || response.LogsTruncated)) return; + try + { + _siteTruncationSink(response.Truncated, response.LogsTruncated); + } + catch (Exception) + { + // Best-effort, like the trace sink. + } + } + private static string? TraceJson(object? value) { if (value is null) return null; diff --git a/src/ZB.MOM.WW.ScadaBridge.ManagementService/FlowDebug/FlowDebugRunner.cs b/src/ZB.MOM.WW.ScadaBridge.ManagementService/FlowDebug/FlowDebugRunner.cs index 37079fc2..60289eed 100644 --- a/src/ZB.MOM.WW.ScadaBridge.ManagementService/FlowDebug/FlowDebugRunner.cs +++ b/src/ZB.MOM.WW.ScadaBridge.ManagementService/FlowDebug/FlowDebugRunner.cs @@ -174,7 +174,19 @@ public sealed class FlowDebugRunner // only known once the script runs. So the operator's scope is pushed into the // RouteHelper and enforced at route time, before any site traffic. var scope = await ResolvePermittedSiteIdentifiersAsync(sp, user, ct).ConfigureAwait(false); - var route = sp.GetRequiredService().WithTraceSink(evt => collector.Add(evt, lowPriority: FlowStepProjector.IsLogLine(evt))).WithSiteScope(scope); + // Gitea #54: a routed site run whose own collector dropped rows reports it here; the flags + // are OR-ed into the result so the page labels the short site lane as truncated. + var siteTruncated = 0; + var siteLogsTruncated = 0; + var route = sp.GetRequiredService() + .WithTraceSink( + evt => collector.Add(evt, lowPriority: FlowStepProjector.IsLogLine(evt)), + (truncated, logsTruncated) => + { + if (truncated) Interlocked.Exchange(ref siteTruncated, 1); + if (logsTruncated) Interlocked.Exchange(ref siteLogsTruncated, 1); + }) + .WithSiteScope(scope); // Step-logs D7: a ScriptLog row rides the collector's low-priority lane so the step rows // emitted at run end are never crowded out of the inline copy (Replay stays complete). var executor = sp.GetRequiredService(); @@ -235,7 +247,8 @@ public sealed class FlowDebugRunner return new FlowDebugRunResult( root, result.Success, result.ErrorMessage, result.Success ? null : result.ErrorCode, - ordered, Truncated: collector.Truncated, startedAt, finishedAt, LogsTruncated: collector.LogsTruncated); + ordered, Truncated: collector.Truncated || Volatile.Read(ref siteTruncated) == 1, startedAt, finishedAt, + LogsTruncated: collector.LogsTruncated || Volatile.Read(ref siteLogsTruncated) == 1); } /// @@ -438,8 +451,10 @@ public sealed class FlowDebugRunner return new FlowDebugRunResult( root, response.Success, response.ErrorMessage, response.Success ? null : faultCode ?? CallFailedCode, - ordered, Truncated: collector.Truncated, startedAt, DateTimeOffset.UtcNow, - LogsTruncated: collector.LogsTruncated); + // Gitea #54: OR in the SITE collector's flags — central's collector only sees the + // rows the site sent, so a site run that dropped rows used to look complete. + ordered, Truncated: collector.Truncated || response.Truncated, startedAt, DateTimeOffset.UtcNow, + LogsTruncated: collector.LogsTruncated || response.LogsTruncated); } /// diff --git a/src/ZB.MOM.WW.ScadaBridge.SiteRuntime/Actors/DeploymentManagerActor.cs b/src/ZB.MOM.WW.ScadaBridge.SiteRuntime/Actors/DeploymentManagerActor.cs index 9a63ba4e..29657541 100644 --- a/src/ZB.MOM.WW.ScadaBridge.SiteRuntime/Actors/DeploymentManagerActor.cs +++ b/src/ZB.MOM.WW.ScadaBridge.SiteRuntime/Actors/DeploymentManagerActor.cs @@ -3357,7 +3357,10 @@ public class DeploymentManagerActor : ReceiveActor, IWithTimers // Flow Debugger (design §5.4): the site run's id and, for a traced // run, its ScriptTrace events — returned inline so a Live run is // instant. Null from an untraced run. - result.ExecutionId, result.TraceEvents); + result.ExecutionId, result.TraceEvents, + // Gitea #54: the site collector's truncation flags ride along so a + // short inline list is labelled short on the Flow Debugger page. + result.Truncated, result.LogsTruncated); } return new RouteToCallResponse( request.CorrelationId, false, null, diff --git a/src/ZB.MOM.WW.ScadaBridge.SiteRuntime/Scripts/ScriptRunLauncher.cs b/src/ZB.MOM.WW.ScadaBridge.SiteRuntime/Scripts/ScriptRunLauncher.cs index 81d6cb94..fdbbf36a 100644 --- a/src/ZB.MOM.WW.ScadaBridge.SiteRuntime/Scripts/ScriptRunLauncher.cs +++ b/src/ZB.MOM.WW.ScadaBridge.SiteRuntime/Scripts/ScriptRunLauncher.cs @@ -452,9 +452,8 @@ internal static class ScriptRunLauncher // Send result to requester if this was an Ask-based call if (!replyTo.IsNobody()) { - replyTo.Tell(new ScriptCallResult( - correlationId, true, state.ReturnValue, null, - context.ExecutionId, collector?.Complete())); + replyTo.Tell(BuildCallResult( + correlationId, true, state.ReturnValue, null, context.ExecutionId, collector)); } // Operational `script` event — execution completed successfully. Same gating as @@ -493,8 +492,8 @@ internal static class ScriptRunLauncher if (!replyTo.IsNobody()) { - replyTo.Tell(new ScriptCallResult( - correlationId, false, null, errorMsg, context?.ExecutionId, collector?.Complete())); + replyTo.Tell(BuildCallResult( + correlationId, false, null, errorMsg, context?.ExecutionId, collector)); } completionTarget.Tell(new ScriptActor.ScriptExecutionCompleted(scriptName, false, errorMsg)); @@ -523,8 +522,8 @@ internal static class ScriptRunLauncher if (!replyTo.IsNobody()) { - replyTo.Tell(new ScriptCallResult( - correlationId, false, null, errorMsg, context?.ExecutionId, collector?.Complete())); + replyTo.Tell(BuildCallResult( + correlationId, false, null, errorMsg, context?.ExecutionId, collector)); } completionTarget.Tell(new ScriptActor.ScriptExecutionCompleted(scriptName, false, errorMsg)); @@ -540,6 +539,22 @@ internal static class ScriptRunLauncher } } + /// + /// Builds the Ask reply for a run. Closes the inline collector FIRST, then reads its + /// truncation flags, so a site run whose collector dropped rows tells the caller its + /// TraceEvents list is short instead of returning a silently short list (Gitea #54). + /// + private static ScriptCallResult BuildCallResult( + string correlationId, bool success, object? returnValue, string? error, + Guid? executionId, FlowTraceCollector? collector) + { + var traceEvents = collector?.Complete(); + return new ScriptCallResult( + correlationId, success, returnValue, error, executionId, traceEvents, + Truncated: collector?.Truncated ?? false, + LogsTruncated: collector?.LogsTruncated ?? false); + } + /// /// Arch-review F7: a traced run that never launched (shed at the in-flight cap, or /// suppressed by MinTimeBetweenRuns) still leaves a Failed diff --git a/tests/ZB.MOM.WW.ScadaBridge.Communication.Tests/SiteCommandSamples.cs b/tests/ZB.MOM.WW.ScadaBridge.Communication.Tests/SiteCommandSamples.cs index f915ef1f..c081dd75 100644 --- a/tests/ZB.MOM.WW.ScadaBridge.Communication.Tests/SiteCommandSamples.cs +++ b/tests/ZB.MOM.WW.ScadaBridge.Communication.Tests/SiteCommandSamples.cs @@ -203,7 +203,10 @@ internal static class SiteCommandSamples // ── Route replies ── Add(new RouteToCallResponse("corr-12", true, 17L, null, T1, G1, TraceSamples()), - new RouteToCallResponse("corr-12", false, null, "script faulted", T2)); + new RouteToCallResponse("corr-12", false, null, "script faulted", T2), + // Gitea #54: fields 8/9 — the site collector's truncation flags. + new RouteToCallResponse("corr-12", true, 17L, null, T1, G1, TraceSamples(), Truncated: true, LogsTruncated: false), + new RouteToCallResponse("corr-12", true, 17L, null, T1, G1, TraceSamples(), Truncated: false, LogsTruncated: true)); Add(new RouteToGetAttributesResponse("corr-13", Parameters(), true, null, T1), new RouteToGetAttributesResponse("corr-13", new Dictionary(), false, "no instance", T2)); Add(new RouteToSetAttributesResponse("corr-14", true, null, T1), diff --git a/tests/ZB.MOM.WW.ScadaBridge.InboundAPI.Tests/RouteHelperTraceTests.cs b/tests/ZB.MOM.WW.ScadaBridge.InboundAPI.Tests/RouteHelperTraceTests.cs index a3d592dd..eb98d91c 100644 --- a/tests/ZB.MOM.WW.ScadaBridge.InboundAPI.Tests/RouteHelperTraceTests.cs +++ b/tests/ZB.MOM.WW.ScadaBridge.InboundAPI.Tests/RouteHelperTraceTests.cs @@ -130,6 +130,45 @@ public class RouteHelperTraceTests Assert.Same(siteEvents[1], sunk[2]); } + /// + /// Gitea #54: a routed reply carrying the site collector's truncation flags is forwarded to the + /// truncation sink, and the sink survives the helper's clone chain (WithDeadline et al.). + /// + /// A task that represents the asynchronous operation. + [Fact] + public async Task TruncationSink_ReceivesTheReplysSiteTruncationFlags_ThroughClones() + { + var child = Guid.NewGuid(); + _router.RouteToCallAsync("SiteA", Arg.Any(), Arg.Any()) + .Returns(ci => new RouteToCallResponse(((RouteToCallRequest)ci[1]).CorrelationId, true, null, null, + DateTimeOffset.UtcNow, child, [SiteEvent(child)], Truncated: false, LogsTruncated: true)); + var reports = new List<(bool, bool)>(); + + await Helper() + .WithTraceSink(_ => { }, (t, lt) => reports.Add((t, lt))) + .WithTrace(true) + .WithDeadline(CancellationToken.None) + .WithSiteScope(null) + .To("inst-1").Call("doWork"); + + Assert.Equal([(false, true)], reports); + } + + /// Gitea #54 control: an untruncated reply does not call the truncation sink. + /// A task that represents the asynchronous operation. + [Fact] + public async Task TruncationSink_IsNotCalled_ForAnUntruncatedReply() + { + _router.RouteToCallAsync("SiteA", Arg.Any(), Arg.Any()) + .Returns(ci => new RouteToCallResponse(((RouteToCallRequest)ci[1]).CorrelationId, true, null, null, + DateTimeOffset.UtcNow)); + var calls = 0; + + await Helper().WithTraceSink(_ => { }, (_, _) => calls++).To("inst-1").Call("doWork"); + + Assert.Equal(0, calls); + } + /// Verifies that a routing failure writes a failed row and still throws. /// A task that represents the asynchronous operation. [Fact] diff --git a/tests/ZB.MOM.WW.ScadaBridge.ManagementService.Tests/FlowDebugRunnerTests.cs b/tests/ZB.MOM.WW.ScadaBridge.ManagementService.Tests/FlowDebugRunnerTests.cs index ef7168b4..6ab896a3 100644 --- a/tests/ZB.MOM.WW.ScadaBridge.ManagementService.Tests/FlowDebugRunnerTests.cs +++ b/tests/ZB.MOM.WW.ScadaBridge.ManagementService.Tests/FlowDebugRunnerTests.cs @@ -307,6 +307,38 @@ public class FlowDebugRunnerTests Assert.Contains(_central, e => e.AsRow().Kind == AuditKind.RouteCall); } + /// + /// Gitea #54: on the method entry the routed reply reaches the runner through RouteHelper's + /// sink, so the site collector's truncation flags must ride that path too. + /// + /// The site's step-row truncation flag. + /// The site's log-row truncation flag. + /// A task that represents the asynchronous operation. + [Theory] + [InlineData(true, false)] + [InlineData(false, true)] + [InlineData(false, false)] + public async Task RunMethod_OrsTheRoutedSitesTruncationFlagsIntoTheResult(bool truncated, bool logsTruncated) + { + var method = new ApiMethod("M", "x") { Id = 7, TimeoutSeconds = 5 }; + _apiRepo.GetApiMethodByIdAsync(7, Arg.Any()).Returns(method); + _locator.GetSiteIdForInstanceAsync("inst", Arg.Any()).Returns("site-a"); + var child = Guid.NewGuid(); + var siteRun = ScadaBridgeAuditEventFactory.Create(AuditChannel.ScriptTrace, AuditKind.ScriptRun, AuditStatus.Delivered, + executionId: child, target: "work"); + _router.RouteToCallAsync("site-a", Arg.Any(), Arg.Any()) + .Returns(ci => new RouteToCallResponse(((RouteToCallRequest)ci[1]).CorrelationId, true, 7, null, + DateTimeOffset.UtcNow, child, [siteRun], truncated, logsTruncated)); + _executor.RegisterHandler("M", ctx => ctx.Route.To("inst").Call("work")); + + var result = await Runner().RunMethodAsync(_services.BuildServiceProvider(), + new FlowDebugRunMethodCommand(7, null), Deployer, CancellationToken.None); + + Assert.True(result.Completed, result.Error); + Assert.Equal(truncated, result.Truncated); + Assert.Equal(logsTruncated, result.LogsTruncated); + } + /// Verifies that a site clock behind central still returns the sink events in emission order. /// A task that represents the asynchronous operation. [Fact] @@ -908,6 +940,35 @@ public class FlowDebugRunnerTests Assert.Equal("OtherScript", result.Events[1].Target); } + /// + /// Gitea #54: a call-kind script run whose SITE collector dropped rows reports truncation on + /// the result, even though central's own collector saw only the (short) list the site sent. + /// + /// The site's step-row truncation flag. + /// The site's log-row truncation flag. + /// A task that represents the asynchronous operation. + [Theory] + [InlineData(true, false)] + [InlineData(false, true)] + [InlineData(false, false)] + public async Task RunScript_CallKind_OrsTheSitesTruncationFlagsIntoTheResult(bool truncated, bool logsTruncated) + { + WireScript(CallScript("Manual", """{"type":"object","properties":{"n":{"type":"integer"}},"required":["n"]}""", timeout: 5)); + var child = Guid.NewGuid(); + var siteRun = ScadaBridgeAuditEventFactory.Create(AuditChannel.ScriptTrace, AuditKind.ScriptRun, AuditStatus.Delivered, + executionId: child, target: "Manual"); + _router.RouteToCallAsync("site-a", Arg.Any(), Arg.Any()) + .Returns(ci => new RouteToCallResponse(((RouteToCallRequest)ci[1]).CorrelationId, true, 42, null, + DateTimeOffset.UtcNow, child, [siteRun], truncated, logsTruncated)); + + var result = await Runner().RunScriptAsync(_services.BuildServiceProvider(), + new FlowDebugRunScriptCommand(5, "Manual", Json("{\"n\":3}"), null), Deployer, CancellationToken.None); + + Assert.True(result.Completed, result.Error); + Assert.Equal(truncated, result.Truncated); + Assert.Equal(logsTruncated, result.LogsTruncated); + } + /// Verifies that a call-kind script run routes a traced call and returns the root route-call row followed by the site rows. /// A task that represents the asynchronous operation. [Fact] diff --git a/tests/ZB.MOM.WW.ScadaBridge.SiteRuntime.Tests/Actors/DeploymentManagerRouteTraceTests.cs b/tests/ZB.MOM.WW.ScadaBridge.SiteRuntime.Tests/Actors/DeploymentManagerRouteTraceTests.cs index 0c02e37a..5ddc0260 100644 --- a/tests/ZB.MOM.WW.ScadaBridge.SiteRuntime.Tests/Actors/DeploymentManagerRouteTraceTests.cs +++ b/tests/ZB.MOM.WW.ScadaBridge.SiteRuntime.Tests/Actors/DeploymentManagerRouteTraceTests.cs @@ -46,9 +46,9 @@ public class DeploymentManagerRouteTraceTests : TestKit, IDisposable TestLocalDb.DeleteFiles(path); } - private IActorRef CreateDeploymentManager() => + private IActorRef CreateDeploymentManager(SiteRuntimeOptions? options = null) => ActorOf(Props.Create(() => new DeploymentManagerActor( - _storage, _compilationService, _sharedScriptLibrary, null, new SiteRuntimeOptions(), + _storage, _compilationService, _sharedScriptLibrary, null, options ?? new SiteRuntimeOptions(), NullLogger.Instance, dclManager: null, healthCollector: null, serviceProvider: null, loggerFactory: null, configFetcher: null))); @@ -56,12 +56,17 @@ public class DeploymentManagerRouteTraceTests : TestKit, IDisposable { InstanceUniqueName = instance, Attributes = [new ResolvedAttribute { CanonicalName = "Mode", Value = "Auto", DataType = "String" }], - Scripts = [new ResolvedScript { CanonicalName = "DoWork", Code = "await Instance.SetAttribute(\"Mode\", \"Manual\"); return 7;" }], + Scripts = + [ + new ResolvedScript { CanonicalName = "DoWork", Code = "await Instance.SetAttribute(\"Mode\", \"Manual\"); return 7;" }, + // Gitea #54: more log lines than the inline collector's low-priority share (450). + new ResolvedScript { CanonicalName = "Chatty", Code = "for (var i = 0; i < 600; i++) Log.Info(\"line \" + i); return 1;" }, + ], }); - private async Task DeployedManager(string instance) + private async Task DeployedManager(string instance, SiteRuntimeOptions? options = null) { - var actor = CreateDeploymentManager(); + var actor = CreateDeploymentManager(options); await Task.Delay(500); actor.Tell(new DeployInstanceCommand("dep", instance, "sha256:x", ConfigJson(instance), "admin", DateTimeOffset.UtcNow)); ExpectMsg(TimeSpan.FromSeconds(5)); @@ -96,6 +101,30 @@ public class DeploymentManagerRouteTraceTests : TestKit, IDisposable Assert.All(rows, r => Assert.Equal(response.ChildExecutionId, r.ExecutionId)); } + /// + /// Gitea #54: a traced routed run whose inline collector dropped log rows carries + /// LogsTruncated on the , so central can label the short + /// site lane; a run that fit reports neither flag. + /// + /// A task that represents the asynchronous operation. + [Fact] + public async Task RouteCall_WithTrace_CarriesTheSiteCollectorsTruncationFlags() + { + var actor = await DeployedManager("ChattyPump", new SiteRuntimeOptions { MaxLogLinesPerRun = 1000 }); + + actor.Tell(new RouteToCallRequest("rc-t", "ChattyPump", "Chatty", null, DateTimeOffset.UtcNow, Trace: true)); + var chatty = ExpectMsg(TimeSpan.FromSeconds(10)); + Assert.True(chatty.Success, chatty.ErrorMessage); + Assert.True(chatty.LogsTruncated); + Assert.False(chatty.Truncated); + + actor.Tell(new RouteToCallRequest("rc-q", "ChattyPump", "DoWork", null, DateTimeOffset.UtcNow, Trace: true)); + var quiet = ExpectMsg(TimeSpan.FromSeconds(10)); + Assert.True(quiet.Success, quiet.ErrorMessage); + Assert.False(quiet.LogsTruncated); + Assert.False(quiet.Truncated); + } + /// Verifies that an untraced route call returns the execution id but no trace events. /// A task that represents the asynchronous operation. [Fact] diff --git a/tests/ZB.MOM.WW.ScadaBridge.SiteRuntime.Tests/Scripts/ScriptRunLauncherTraceTests.cs b/tests/ZB.MOM.WW.ScadaBridge.SiteRuntime.Tests/Scripts/ScriptRunLauncherTraceTests.cs index cc3535c0..b6049843 100644 --- a/tests/ZB.MOM.WW.ScadaBridge.SiteRuntime.Tests/Scripts/ScriptRunLauncherTraceTests.cs +++ b/tests/ZB.MOM.WW.ScadaBridge.SiteRuntime.Tests/Scripts/ScriptRunLauncherTraceTests.cs @@ -161,4 +161,38 @@ public class ScriptRunLauncherTraceTests : TestKit, IDisposable Assert.All(logs, e => Assert.True(rows.ToList().IndexOf(e) < anchorIndex)); Assert.Equal(logs.Count, anchorIndex); } + + // ── (d) the reply reports the site collector's truncation (Gitea #54) ──────── + + /// + /// Verifies that a traced run whose inline collector dropped log-line rows says so on the + /// reply (LogsTruncated), instead of handing the caller a silently short list. + /// + [Fact] + public void TracedRun_WhoseCollectorDroppedLogRows_ReportsLogsTruncated() + { + // 600 lines is under the per-run line cap set here (1000) but over the collector's + // low-priority event share (DefaultMaxEvents - LowPriorityReserveEvents = 450). + var result = RunTraced( + "for (var i = 0; i < 600; i++) Log.Info(\"line \" + i); return null;", + Options(maxLogLines: 1000)); + + Assert.True(result.Success); + Assert.True(result.LogsTruncated); + Assert.False(result.Truncated); + Assert.True(result.TraceEvents!.Count < 600); + // Step rows still fit: the ScriptRun anchor is present. + ScriptRunRow(result); + } + + /// Positive control: a small traced run reports neither truncation flag. + [Fact] + public void TracedRun_WithinTheCollectorCaps_ReportsNoTruncation() + { + var result = RunTraced("Log.Info(\"one\"); return null;", Options()); + + Assert.True(result.Success); + Assert.False(result.LogsTruncated); + Assert.False(result.Truncated); + } }