UNPKG

jspurefix

Version:
89 lines (81 loc) 3.88 kB
import { AsciiChars, AsciiEncoder, TimeFormatter } from '../../buffer/ascii' import { MsgTransmitter } from '../msg-transmitter' import { ILooseObject } from '../../collections/collection' import { IContainedSet } from '../../dictionary/contained' import { MessageDefinition } from '../../dictionary/definition' import { IJsFixConfig } from '../../config' import { IStandardHeader } from '../../types/FIX4.4/repo' import { inject, injectable } from 'tsyringe' import { DITokens } from '../../runtime/di-tokens' import { ElasticBuffer } from '../../buffer' @injectable() export class AsciiMsgTransmitter extends MsgTransmitter { public msgSeqNum: number public time: Date private readonly header: IContainedSet | undefined private readonly trailer: IContainedSet | undefined constructor (@inject(DITokens.IJsFixConfig) public readonly config: IJsFixConfig) { super(config.sessionContainer.resolve<ElasticBuffer>(DITokens.TransmitBuffer), config.definitions, config.description) this.msgSeqNum = (config.description.LastSentSeqNum ?? 0) + 1 // adding 1 as this the next sequence # to use. const buffer = this.buffer const tf: TimeFormatter = new TimeFormatter(buffer) this.encoder = new AsciiEncoder(buffer, config.definitions, tf, config.delimiter ?? AsciiChars.Soh, config.logDelimiter ?? AsciiChars.Pipe) const components = config.definitions.component this.header = components.get('StandardHeader') this.trailer = components.get('StandardTrailer') } private checksum (): number { const buffer = this.buffer const encoder: AsciiEncoder = this.encoder as AsciiEncoder let checksum: number = buffer.sum() if (encoder.delimiter !== encoder.logDelimiter) { const changes = encoder.tags.nextTagPos checksum -= changes * encoder.logDelimiter checksum += changes * encoder.delimiter } checksum = checksum % 256 return checksum } public encodeMessage (msgType: string, obj: ILooseObject): (ILooseObject | null) { const encoder: AsciiEncoder = this.encoder as AsciiEncoder const factory = this.config.factory let headerProps: Partial<IStandardHeader> = {} const { StandardHeader, ...bodyProps } = obj if (StandardHeader) { const { BeginString, BodyLength, MsgType, SenderCompID, SendingTime, TargetCompID, TargetSubID, ...hp } = StandardHeader headerProps = hp // pick up any optional applied by application headerProps.OrigSendingTime = SendingTime // when first sent } const msgSeqNum = this.msgSeqNum const sendingTime = this.time || new Date() const hdr: ILooseObject | null = factory?.header(msgType, msgSeqNum, sendingTime, headerProps) ?? null // Only increment sequence number if this is not a duplicate message. if (!hdr) return null if (!headerProps.PossDupFlag) { this.msgSeqNum++ } const buffer = this.buffer buffer.reset() const msgDef: MessageDefinition | undefined = this.definitions.message.get(msgType) if (!msgDef) { this.emit('error', new Error(`ascii transmitter cannot find definition for ${msgType}`)) return null } const headerName: string = this.header?.name ?? 'header' const trailerName: string = this.trailer?.name ?? 'trailer' encoder.encode(hdr, headerName) encoder.encode(bodyProps, msgDef.name) const lenPos = encoder.bodyLengthPos const bodyLength: number = Math.max(4, this.config.description.BodyLengthChars ?? 7) const len = buffer.getPos() - encoder.msgTypePos buffer.patchPaddedNumberAtPos(lenPos, len, bodyLength) const checksum: number = this.checksum() const trl: ILooseObject | null = factory?.trailer(checksum) ?? null if (trl) { encoder.encode(trl, trailerName) } return hdr } }