sendMessage method

Future<IsoMsg?> sendMessage(
  1. IsoMsg message, {
  2. Duration? timeout,
  3. IsoLinkEvents? events,
  4. Future<void> beforeSend(
    1. IsoMsg request
    )?,
})

MACs, frames and sends message, then waits for its response.

beforeSend runs after the header is attached and before the bytes leave, which is where a reversible transaction stores its reversal record.

Implementation

Future<IsoMsg?> sendMessage(
  IsoMsg message, {
  Duration? timeout,
  IsoLinkEvents? events,
  Future<void> Function(IsoMsg request)? beforeSend,
}) async {
  final Duration effectiveTimeout = timeout ?? Duration(seconds: config.defaultTxnTimeoutSeconds);

  try {
    await macComponent.generateMac(message, 4);
    IsoTrace.log(IsoTraceKind.mac, "request MAC built with the ${macComponent.usesDefaultMac(message) ? "default (KTM) " : "session "}key", bytes: message.getBytes(64));
  } catch (error) {
    IsoTrace.log(IsoTraceKind.mac, "the PED could not build a MAC: $error");
    throw UIsoException(UIsoErrorCode.securityModuleFailed, "could not build the request MAC", error);
  }

  message.recalcBitMap();

  final OssAcqHeader header = OssAcqHeader(source: config.sourceId, destination: config.destinationId)
    ..flags = 0x00
    ..sequence = OssAcqHeader.generateSequence();
  message.isoHeader = header.header;

  await _ensureConnected(events);
  if (beforeSend != null) await beforeSend(message);

  events?.onWaitForReceive();

  final String key = OssFrameCodec.messageKey(message);
  IsoTrace.log(IsoTraceKind.out, "mti=${message.mti} pc=${message.getString(3)} stan=${message.getString(11)} key=$key");

  final IsoMsg? response = await _multiplexer.request(
    key,
    () async {
      final Uint8List frame = _codec.encode(message);
      IsoTrace.log(IsoTraceKind.out, "frame out, ${frame.length} bytes", bytes: frame);
      await _transport!.send(frame);
    },
    effectiveTimeout,
  );

  if (response == null) {
    IsoTrace.log(IsoTraceKind.result, "nothing came back within ${effectiveTimeout.inSeconds}s");
    events?.onReceiveTimeout();
    return null;
  }

  if (!response.isReject) {
    final bool macValid = await macComponent.checkMac(response, 4);
    if (!macValid) {
      IsoTrace.log(IsoTraceKind.result, "the host answered but its MAC did not verify — the response is discarded");
      throw UIsoException(UIsoErrorCode.badResponseMac, "the host answered but the response MAC did not verify");
    }
    IsoTrace.log(IsoTraceKind.mac, "response MAC verified");
  } else {
    IsoTrace.log(IsoTraceKind.result, "the host rejected the message, reject code ${response.rejectCode}");
  }
  return response;
}