@ubercode/dcmtk 0.7.3 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +596 -290
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +198 -57
- package/dist/index.d.ts +198 -57
- package/dist/index.js +596 -291
- package/dist/index.js.map +1 -1
- package/dist/tools.cjs +38 -0
- package/dist/tools.cjs.map +1 -1
- package/dist/tools.d.cts +14 -0
- package/dist/tools.d.ts +14 -0
- package/dist/tools.js +38 -0
- package/dist/tools.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -33106,6 +33106,30 @@ async function echoscu(options) {
|
|
|
33106
33106
|
return ok({ success: true, stderr: result.value.stderr });
|
|
33107
33107
|
}
|
|
33108
33108
|
var VERBOSITY_FLAGS19 = { verbose: "-v", debug: "-d" };
|
|
33109
|
+
var DECOMPRESS_FLAGS = {
|
|
33110
|
+
never: "--decompress-never",
|
|
33111
|
+
lossless: "--decompress-lossless",
|
|
33112
|
+
lossy: "--decompress-lossy"
|
|
33113
|
+
};
|
|
33114
|
+
function pushSendModeArgs(args, options) {
|
|
33115
|
+
if (options.noHalt === true) {
|
|
33116
|
+
args.push("--no-halt");
|
|
33117
|
+
}
|
|
33118
|
+
if (options.noIllegalProposal === true) {
|
|
33119
|
+
args.push("--no-illegal-proposal");
|
|
33120
|
+
}
|
|
33121
|
+
if (options.decompress !== void 0) {
|
|
33122
|
+
args.push(DECOMPRESS_FLAGS[options.decompress]);
|
|
33123
|
+
}
|
|
33124
|
+
if (options.multiAssociations === true) {
|
|
33125
|
+
args.push("+ma");
|
|
33126
|
+
} else if (options.multiAssociations === false) {
|
|
33127
|
+
args.push("-ma");
|
|
33128
|
+
}
|
|
33129
|
+
if (options.createReportFile !== void 0) {
|
|
33130
|
+
args.push("--create-report-file", options.createReportFile);
|
|
33131
|
+
}
|
|
33132
|
+
}
|
|
33109
33133
|
var DcmsendOptionsSchema = z.object({
|
|
33110
33134
|
timeoutMs: z.number().int().positive().optional(),
|
|
33111
33135
|
signal: z.instanceof(AbortSignal).optional(),
|
|
@@ -33115,8 +33139,15 @@ var DcmsendOptionsSchema = z.object({
|
|
|
33115
33139
|
callingAETitle: z.string().min(1).max(16).refine(isValidAETitle, { message: "AE Title contains invalid characters" }).optional(),
|
|
33116
33140
|
calledAETitle: z.string().min(1).max(16).refine(isValidAETitle, { message: "AE Title contains invalid characters" }).optional(),
|
|
33117
33141
|
scanDirectory: z.boolean().optional(),
|
|
33142
|
+
recurse: z.boolean().optional(),
|
|
33143
|
+
scanPattern: z.string().min(1).optional(),
|
|
33118
33144
|
verbosity: z.enum(["verbose", "debug"]).optional(),
|
|
33119
33145
|
noUidChecks: z.boolean().optional(),
|
|
33146
|
+
noHalt: z.boolean().optional(),
|
|
33147
|
+
noIllegalProposal: z.boolean().optional(),
|
|
33148
|
+
decompress: z.enum(["never", "lossless", "lossy"]).optional(),
|
|
33149
|
+
multiAssociations: z.boolean().optional(),
|
|
33150
|
+
createReportFile: z.string().min(1).optional(),
|
|
33120
33151
|
maxPduReceive: z.number().int().min(4096).max(131072).optional(),
|
|
33121
33152
|
maxPduSend: z.number().int().min(4096).max(131072).optional(),
|
|
33122
33153
|
noHostnameLookup: z.boolean().optional(),
|
|
@@ -33162,6 +33193,13 @@ function buildArgs33(options) {
|
|
|
33162
33193
|
if (options.scanDirectory === true) {
|
|
33163
33194
|
args.push("--scan-directories");
|
|
33164
33195
|
}
|
|
33196
|
+
if (options.recurse === true) {
|
|
33197
|
+
args.push("+r");
|
|
33198
|
+
}
|
|
33199
|
+
if (options.scanPattern !== void 0) {
|
|
33200
|
+
args.push("--scan-pattern", options.scanPattern);
|
|
33201
|
+
}
|
|
33202
|
+
pushSendModeArgs(args, options);
|
|
33165
33203
|
args.push(options.host, String(options.port));
|
|
33166
33204
|
args.push(...options.files);
|
|
33167
33205
|
return args;
|
|
@@ -36852,68 +36890,13 @@ var SenderHealth = {
|
|
|
36852
36890
|
DOWN: "down"
|
|
36853
36891
|
};
|
|
36854
36892
|
|
|
36855
|
-
// src/senders/
|
|
36856
|
-
var DEFAULT_MAX_ASSOCIATIONS = 4;
|
|
36857
|
-
var DEFAULT_MAX_QUEUE_LENGTH = 1e3;
|
|
36858
|
-
var DEFAULT_MAX_RETRIES = 3;
|
|
36859
|
-
var DEFAULT_RETRY_DELAY_MS = 1e3;
|
|
36860
|
-
var DEFAULT_BUCKET_FLUSH_MS = 5e3;
|
|
36861
|
-
var DEFAULT_MAX_BUCKET_SIZE = 50;
|
|
36862
|
-
var MAX_ASSOCIATIONS_LIMIT = 64;
|
|
36893
|
+
// src/senders/SenderEngine.ts
|
|
36863
36894
|
var DEGRADE_THRESHOLD = 3;
|
|
36864
36895
|
var DOWN_THRESHOLD = 10;
|
|
36865
36896
|
var RECOVERY_THRESHOLD = 3;
|
|
36866
|
-
var
|
|
36867
|
-
|
|
36868
|
-
|
|
36869
|
-
calledAETitle: z.string().min(1).max(16).refine(isValidAETitle, { message: "AE Title contains invalid characters" }).optional(),
|
|
36870
|
-
callingAETitle: z.string().min(1).max(16).refine(isValidAETitle, { message: "AE Title contains invalid characters" }).optional(),
|
|
36871
|
-
mode: z.enum(["single", "multiple", "bucket"]).optional(),
|
|
36872
|
-
maxAssociations: z.number().int().min(1).max(MAX_ASSOCIATIONS_LIMIT).optional(),
|
|
36873
|
-
proposedTransferSyntax: z.union([z.enum(PROPOSED_TS_VALUES), z.array(z.enum(PROPOSED_TS_VALUES)).min(1)]).optional(),
|
|
36874
|
-
combineProposedTransferSyntaxes: z.boolean().optional(),
|
|
36875
|
-
maxQueueLength: z.number().int().min(1).optional(),
|
|
36876
|
-
timeoutMs: z.number().int().positive().optional(),
|
|
36877
|
-
maxRetries: z.number().int().min(0).optional(),
|
|
36878
|
-
retryDelayMs: z.number().int().min(0).optional(),
|
|
36879
|
-
bucketFlushMs: z.number().int().positive().optional(),
|
|
36880
|
-
maxBucketSize: z.number().int().min(1).optional(),
|
|
36881
|
-
maxPduReceive: z.number().int().min(4096).max(131072).optional(),
|
|
36882
|
-
maxPduSend: z.number().int().min(4096).max(131072).optional(),
|
|
36883
|
-
associationTimeout: z.number().int().positive().optional(),
|
|
36884
|
-
acseTimeout: z.number().int().positive().optional(),
|
|
36885
|
-
dimseTimeout: z.number().int().positive().optional(),
|
|
36886
|
-
noHostnameLookup: z.boolean().optional(),
|
|
36887
|
-
verbosity: z.enum(["verbose", "debug"]).optional(),
|
|
36888
|
-
required: z.boolean().optional(),
|
|
36889
|
-
signal: z.instanceof(AbortSignal).optional()
|
|
36890
|
-
}).strict();
|
|
36891
|
-
function resolveConfig(options) {
|
|
36892
|
-
const mode = options.mode ?? "multiple";
|
|
36893
|
-
const rawMax = options.maxAssociations ?? DEFAULT_MAX_ASSOCIATIONS;
|
|
36894
|
-
return {
|
|
36895
|
-
mode,
|
|
36896
|
-
configuredMaxAssociations: mode === "single" ? 1 : rawMax,
|
|
36897
|
-
maxQueueLength: options.maxQueueLength ?? DEFAULT_MAX_QUEUE_LENGTH,
|
|
36898
|
-
defaultTimeoutMs: options.timeoutMs ?? DEFAULT_TIMEOUT_MS,
|
|
36899
|
-
defaultMaxRetries: options.maxRetries ?? DEFAULT_MAX_RETRIES,
|
|
36900
|
-
retryDelayMs: options.retryDelayMs ?? DEFAULT_RETRY_DELAY_MS,
|
|
36901
|
-
bucketFlushMs: options.bucketFlushMs ?? DEFAULT_BUCKET_FLUSH_MS,
|
|
36902
|
-
maxBucketSize: options.maxBucketSize ?? DEFAULT_MAX_BUCKET_SIZE
|
|
36903
|
-
};
|
|
36904
|
-
}
|
|
36905
|
-
var DicomSender = class _DicomSender extends EventEmitter {
|
|
36906
|
-
constructor(options) {
|
|
36907
|
-
super();
|
|
36908
|
-
__publicField(this, "options");
|
|
36909
|
-
__publicField(this, "mode");
|
|
36910
|
-
__publicField(this, "configuredMaxAssociations");
|
|
36911
|
-
__publicField(this, "maxQueueLength");
|
|
36912
|
-
__publicField(this, "defaultTimeoutMs");
|
|
36913
|
-
__publicField(this, "defaultMaxRetries");
|
|
36914
|
-
__publicField(this, "retryDelayMs");
|
|
36915
|
-
__publicField(this, "bucketFlushMs");
|
|
36916
|
-
__publicField(this, "maxBucketSize");
|
|
36897
|
+
var SenderEngine = class {
|
|
36898
|
+
constructor(config) {
|
|
36899
|
+
__publicField(this, "config");
|
|
36917
36900
|
// Queue and concurrency state
|
|
36918
36901
|
__publicField(this, "queue", []);
|
|
36919
36902
|
__publicField(this, "activeAssociations", 0);
|
|
@@ -36926,113 +36909,36 @@ var DicomSender = class _DicomSender extends EventEmitter {
|
|
|
36926
36909
|
// Bucket state (bucket mode only)
|
|
36927
36910
|
__publicField(this, "currentBucket", []);
|
|
36928
36911
|
__publicField(this, "bucketTimer");
|
|
36929
|
-
|
|
36930
|
-
|
|
36931
|
-
this.setMaxListeners(20);
|
|
36932
|
-
this.on("error", () => {
|
|
36933
|
-
});
|
|
36934
|
-
this.options = options;
|
|
36935
|
-
const cfg = resolveConfig(options);
|
|
36936
|
-
this.mode = cfg.mode;
|
|
36937
|
-
this.configuredMaxAssociations = cfg.configuredMaxAssociations;
|
|
36938
|
-
this.effectiveMaxAssociations = cfg.configuredMaxAssociations;
|
|
36939
|
-
this.maxQueueLength = cfg.maxQueueLength;
|
|
36940
|
-
this.defaultTimeoutMs = cfg.defaultTimeoutMs;
|
|
36941
|
-
this.defaultMaxRetries = cfg.defaultMaxRetries;
|
|
36942
|
-
this.retryDelayMs = cfg.retryDelayMs;
|
|
36943
|
-
this.bucketFlushMs = cfg.bucketFlushMs;
|
|
36944
|
-
this.maxBucketSize = cfg.maxBucketSize;
|
|
36945
|
-
if (options.signal !== void 0) {
|
|
36946
|
-
this.wireAbortSignal(options.signal);
|
|
36947
|
-
}
|
|
36912
|
+
this.config = config;
|
|
36913
|
+
this.effectiveMaxAssociations = config.configuredMaxAssociations;
|
|
36948
36914
|
}
|
|
36949
36915
|
// -----------------------------------------------------------------------
|
|
36950
36916
|
// Public API
|
|
36951
36917
|
// -----------------------------------------------------------------------
|
|
36952
|
-
/**
|
|
36953
|
-
|
|
36954
|
-
*
|
|
36955
|
-
* @param options - Configuration options
|
|
36956
|
-
* @returns A Result containing the instance or a validation error
|
|
36957
|
-
*/
|
|
36958
|
-
static create(options) {
|
|
36959
|
-
const validation = DicomSenderOptionsSchema.safeParse(options);
|
|
36960
|
-
if (!validation.success) {
|
|
36961
|
-
return err(createValidationError("DicomSender", validation.error));
|
|
36962
|
-
}
|
|
36963
|
-
return ok(new _DicomSender(options));
|
|
36964
|
-
}
|
|
36965
|
-
/**
|
|
36966
|
-
* Sends one or more DICOM files to the remote endpoint.
|
|
36967
|
-
*
|
|
36968
|
-
* In single/multiple mode, files are sent as one storescu call.
|
|
36969
|
-
* In bucket mode, files are accumulated into a bucket and flushed
|
|
36970
|
-
* when the bucket reaches maxBucketSize or the flush timer fires.
|
|
36971
|
-
*
|
|
36972
|
-
* The returned promise resolves when the files are actually sent
|
|
36973
|
-
* (not just queued). Callers can await for confirmation or
|
|
36974
|
-
* fire-and-forget with `void sender.send(files)`.
|
|
36975
|
-
*
|
|
36976
|
-
* @param files - One or more DICOM file paths
|
|
36977
|
-
* @param options - Per-send overrides
|
|
36978
|
-
* @returns A Result containing the send result or an error
|
|
36979
|
-
*/
|
|
36980
|
-
send(files, options) {
|
|
36918
|
+
/** Sends files through the engine's queue/bucket system. */
|
|
36919
|
+
send(files, timeoutMs, maxRetries, binaryParams) {
|
|
36981
36920
|
if (this.isStopped) {
|
|
36982
|
-
return Promise.resolve(err(new Error(
|
|
36921
|
+
return Promise.resolve(err(new Error(`${this.config.senderName}: sender is stopped`)));
|
|
36983
36922
|
}
|
|
36984
36923
|
if (files.length === 0) {
|
|
36985
|
-
return Promise.resolve(err(new Error(
|
|
36986
|
-
}
|
|
36987
|
-
return this.dispatchSend(files, this.buildSendParams(options));
|
|
36988
|
-
}
|
|
36989
|
-
/** Builds SendParams from per-send options, applying instance defaults. */
|
|
36990
|
-
buildSendParams(options) {
|
|
36991
|
-
return {
|
|
36992
|
-
timeoutMs: options?.timeoutMs ?? this.defaultTimeoutMs,
|
|
36993
|
-
maxRetries: options?.maxRetries ?? this.defaultMaxRetries,
|
|
36994
|
-
calledAETitle: options?.calledAETitle,
|
|
36995
|
-
callingAETitle: options?.callingAETitle,
|
|
36996
|
-
required: options?.required,
|
|
36997
|
-
proposedTransferSyntax: options?.proposedTransferSyntax,
|
|
36998
|
-
combineProposedTransferSyntaxes: options?.combineProposedTransferSyntaxes
|
|
36999
|
-
};
|
|
37000
|
-
}
|
|
37001
|
-
/** Dispatches a send to the appropriate mode handler. */
|
|
37002
|
-
dispatchSend(files, params) {
|
|
37003
|
-
switch (this.mode) {
|
|
37004
|
-
case "single":
|
|
37005
|
-
case "multiple":
|
|
37006
|
-
return this.enqueueSend(files, params);
|
|
37007
|
-
case "bucket":
|
|
37008
|
-
return this.enqueueBucket(files, params);
|
|
37009
|
-
default:
|
|
37010
|
-
assertUnreachable(this.mode);
|
|
36924
|
+
return Promise.resolve(err(new Error(`${this.config.senderName}: no files provided`)));
|
|
37011
36925
|
}
|
|
36926
|
+
return this.dispatchSend(files, timeoutMs, maxRetries, binaryParams);
|
|
37012
36927
|
}
|
|
37013
|
-
/**
|
|
37014
|
-
* Flushes the current bucket immediately (bucket mode only).
|
|
37015
|
-
* In single/multiple mode this is a no-op.
|
|
37016
|
-
*/
|
|
36928
|
+
/** Flushes the current bucket immediately (bucket mode only). */
|
|
37017
36929
|
flush() {
|
|
37018
|
-
if (this.mode !== "bucket") return;
|
|
36930
|
+
if (this.config.mode !== "bucket") return;
|
|
37019
36931
|
if (this.currentBucket.length === 0) return;
|
|
37020
36932
|
this.clearBucketTimer();
|
|
37021
36933
|
this.flushBucketInternal("timer");
|
|
37022
36934
|
}
|
|
37023
|
-
/**
|
|
37024
|
-
* Gracefully stops the sender. Rejects all queued items and
|
|
37025
|
-
* waits for active associations to complete.
|
|
37026
|
-
*/
|
|
36935
|
+
/** Gracefully stops the engine. Rejects all queued items and waits for active associations. */
|
|
37027
36936
|
async stop() {
|
|
37028
36937
|
if (this.isStopped) return;
|
|
37029
36938
|
this.isStopped = true;
|
|
37030
|
-
if (this.options.signal !== void 0 && this.abortHandler !== void 0) {
|
|
37031
|
-
this.options.signal.removeEventListener("abort", this.abortHandler);
|
|
37032
|
-
}
|
|
37033
36939
|
this.clearBucketTimer();
|
|
37034
|
-
this.rejectBucket(
|
|
37035
|
-
this.rejectQueue(
|
|
36940
|
+
this.rejectBucket(`${this.config.senderName}: sender stopped`);
|
|
36941
|
+
this.rejectQueue(`${this.config.senderName}: sender stopped`);
|
|
37036
36942
|
await this.waitForActive();
|
|
37037
36943
|
}
|
|
37038
36944
|
/** Current sender status. */
|
|
@@ -37047,77 +36953,37 @@ var DicomSender = class _DicomSender extends EventEmitter {
|
|
|
37047
36953
|
stopped: this.isStopped
|
|
37048
36954
|
};
|
|
37049
36955
|
}
|
|
36956
|
+
/** Whether the engine has been stopped. */
|
|
36957
|
+
get stopped() {
|
|
36958
|
+
return this.isStopped;
|
|
36959
|
+
}
|
|
37050
36960
|
// -----------------------------------------------------------------------
|
|
37051
|
-
//
|
|
36961
|
+
// Dispatch
|
|
37052
36962
|
// -----------------------------------------------------------------------
|
|
37053
|
-
/**
|
|
37054
|
-
|
|
37055
|
-
|
|
37056
|
-
|
|
37057
|
-
|
|
37058
|
-
|
|
37059
|
-
|
|
37060
|
-
|
|
37061
|
-
|
|
37062
|
-
|
|
37063
|
-
|
|
37064
|
-
* Registers a listener for successful sends.
|
|
37065
|
-
*
|
|
37066
|
-
* @param listener - Callback receiving send complete data
|
|
37067
|
-
* @returns this for chaining
|
|
37068
|
-
*/
|
|
37069
|
-
onSendComplete(listener) {
|
|
37070
|
-
return this.on("SEND_COMPLETE", listener);
|
|
37071
|
-
}
|
|
37072
|
-
/**
|
|
37073
|
-
* Registers a listener for failed sends.
|
|
37074
|
-
*
|
|
37075
|
-
* @param listener - Callback receiving send failed data
|
|
37076
|
-
* @returns this for chaining
|
|
37077
|
-
*/
|
|
37078
|
-
onSendFailed(listener) {
|
|
37079
|
-
return this.on("SEND_FAILED", listener);
|
|
37080
|
-
}
|
|
37081
|
-
/**
|
|
37082
|
-
* Registers a listener for health state changes.
|
|
37083
|
-
*
|
|
37084
|
-
* @param listener - Callback receiving health change data
|
|
37085
|
-
* @returns this for chaining
|
|
37086
|
-
*/
|
|
37087
|
-
onHealthChanged(listener) {
|
|
37088
|
-
return this.on("HEALTH_CHANGED", listener);
|
|
37089
|
-
}
|
|
37090
|
-
/**
|
|
37091
|
-
* Registers a listener for bucket flushes (bucket mode only).
|
|
37092
|
-
*
|
|
37093
|
-
* @param listener - Callback receiving bucket flush data
|
|
37094
|
-
* @returns this for chaining
|
|
37095
|
-
*/
|
|
37096
|
-
onBucketFlushed(listener) {
|
|
37097
|
-
return this.on("BUCKET_FLUSHED", listener);
|
|
36963
|
+
/** Dispatches a send to the appropriate mode handler. */
|
|
36964
|
+
dispatchSend(files, timeoutMs, maxRetries, binaryParams) {
|
|
36965
|
+
switch (this.config.mode) {
|
|
36966
|
+
case "single":
|
|
36967
|
+
case "multiple":
|
|
36968
|
+
return this.enqueueSend(files, timeoutMs, maxRetries, binaryParams);
|
|
36969
|
+
case "bucket":
|
|
36970
|
+
return this.enqueueBucket(files, timeoutMs, maxRetries, binaryParams);
|
|
36971
|
+
default:
|
|
36972
|
+
assertUnreachable(this.config.mode);
|
|
36973
|
+
}
|
|
37098
36974
|
}
|
|
37099
36975
|
// -----------------------------------------------------------------------
|
|
37100
36976
|
// Single/Multiple mode: queue-based dispatch
|
|
37101
36977
|
// -----------------------------------------------------------------------
|
|
37102
36978
|
/** Enqueues a send and dispatches immediately if capacity allows. */
|
|
37103
|
-
enqueueSend(files,
|
|
36979
|
+
enqueueSend(files, timeoutMs, maxRetries, binaryParams) {
|
|
37104
36980
|
return new Promise((resolve) => {
|
|
37105
36981
|
const totalQueued = this.queue.length + this.currentBucket.length;
|
|
37106
|
-
if (totalQueued >= this.maxQueueLength) {
|
|
37107
|
-
resolve(err(new Error(
|
|
36982
|
+
if (totalQueued >= this.config.maxQueueLength) {
|
|
36983
|
+
resolve(err(new Error(`${this.config.senderName}: queue full`)));
|
|
37108
36984
|
return;
|
|
37109
36985
|
}
|
|
37110
|
-
const entry = {
|
|
37111
|
-
files,
|
|
37112
|
-
timeoutMs: params.timeoutMs,
|
|
37113
|
-
maxRetries: params.maxRetries,
|
|
37114
|
-
calledAETitle: params.calledAETitle,
|
|
37115
|
-
callingAETitle: params.callingAETitle,
|
|
37116
|
-
required: params.required,
|
|
37117
|
-
proposedTransferSyntax: params.proposedTransferSyntax,
|
|
37118
|
-
combineProposedTransferSyntaxes: params.combineProposedTransferSyntaxes,
|
|
37119
|
-
resolve
|
|
37120
|
-
};
|
|
36986
|
+
const entry = { files, timeoutMs, maxRetries, binaryParams, resolve };
|
|
37121
36987
|
if (this.activeAssociations < this.effectiveMaxAssociations) {
|
|
37122
36988
|
void this.executeEntry(entry);
|
|
37123
36989
|
} else {
|
|
@@ -37137,27 +37003,16 @@ var DicomSender = class _DicomSender extends EventEmitter {
|
|
|
37137
37003
|
// Bucket mode: accumulate-then-flush
|
|
37138
37004
|
// -----------------------------------------------------------------------
|
|
37139
37005
|
/** Adds files to the current bucket and triggers flush if full. */
|
|
37140
|
-
enqueueBucket(files,
|
|
37006
|
+
enqueueBucket(files, timeoutMs, maxRetries, binaryParams) {
|
|
37141
37007
|
return new Promise((resolve) => {
|
|
37142
37008
|
const totalQueued = this.queue.length + this.currentBucket.length;
|
|
37143
|
-
if (totalQueued >= this.maxQueueLength) {
|
|
37144
|
-
resolve(err(new Error(
|
|
37009
|
+
if (totalQueued >= this.config.maxQueueLength) {
|
|
37010
|
+
resolve(err(new Error(`${this.config.senderName}: queue full`)));
|
|
37145
37011
|
return;
|
|
37146
37012
|
}
|
|
37147
|
-
|
|
37148
|
-
this.currentBucket.push({
|
|
37149
|
-
files,
|
|
37150
|
-
resolve,
|
|
37151
|
-
timeoutMs,
|
|
37152
|
-
maxRetries,
|
|
37153
|
-
calledAETitle,
|
|
37154
|
-
callingAETitle,
|
|
37155
|
-
required,
|
|
37156
|
-
proposedTransferSyntax,
|
|
37157
|
-
combineProposedTransferSyntaxes
|
|
37158
|
-
});
|
|
37013
|
+
this.currentBucket.push({ files, resolve, timeoutMs, maxRetries, binaryParams });
|
|
37159
37014
|
const totalFiles = this.countBucketFiles();
|
|
37160
|
-
if (totalFiles >= this.maxBucketSize) {
|
|
37015
|
+
if (totalFiles >= this.config.maxBucketSize) {
|
|
37161
37016
|
this.clearBucketTimer();
|
|
37162
37017
|
void this.flushBucketInternal("maxSize");
|
|
37163
37018
|
} else {
|
|
@@ -37189,11 +37044,7 @@ var DicomSender = class _DicomSender extends EventEmitter {
|
|
|
37189
37044
|
files: allFiles,
|
|
37190
37045
|
timeoutMs,
|
|
37191
37046
|
maxRetries,
|
|
37192
|
-
|
|
37193
|
-
callingAETitle: entries[0]?.callingAETitle,
|
|
37194
|
-
required: entries[0]?.required,
|
|
37195
|
-
proposedTransferSyntax: entries[0]?.proposedTransferSyntax,
|
|
37196
|
-
combineProposedTransferSyntaxes: entries[0]?.combineProposedTransferSyntaxes,
|
|
37047
|
+
binaryParams: entries[0].binaryParams,
|
|
37197
37048
|
resolve: (result) => {
|
|
37198
37049
|
for (let i = 0; i < entries.length; i++) {
|
|
37199
37050
|
entries[i].resolve(result);
|
|
@@ -37207,7 +37058,7 @@ var DicomSender = class _DicomSender extends EventEmitter {
|
|
|
37207
37058
|
const entries = [...this.currentBucket];
|
|
37208
37059
|
this.currentBucket = [];
|
|
37209
37060
|
const bucketEntry = this.mergeBucketEntries(entries);
|
|
37210
|
-
this.
|
|
37061
|
+
this.config.emitters.emitBucketFlushed({ fileCount: bucketEntry.files.length, reason });
|
|
37211
37062
|
if (this.activeAssociations < this.effectiveMaxAssociations) {
|
|
37212
37063
|
void this.executeEntry(bucketEntry);
|
|
37213
37064
|
} else {
|
|
@@ -37220,7 +37071,7 @@ var DicomSender = class _DicomSender extends EventEmitter {
|
|
|
37220
37071
|
this.bucketTimer = setTimeout(() => {
|
|
37221
37072
|
this.bucketTimer = void 0;
|
|
37222
37073
|
void this.flushBucketInternal("timer");
|
|
37223
|
-
}, this.bucketFlushMs);
|
|
37074
|
+
}, this.config.bucketFlushMs);
|
|
37224
37075
|
}
|
|
37225
37076
|
/** Clears the bucket flush timer. */
|
|
37226
37077
|
clearBucketTimer() {
|
|
@@ -37232,7 +37083,7 @@ var DicomSender = class _DicomSender extends EventEmitter {
|
|
|
37232
37083
|
// -----------------------------------------------------------------------
|
|
37233
37084
|
// Core send execution with retry
|
|
37234
37085
|
// -----------------------------------------------------------------------
|
|
37235
|
-
/** Executes a single queue entry: calls
|
|
37086
|
+
/** Executes a single queue entry: calls the binary with retry. */
|
|
37236
37087
|
async executeEntry(entry) {
|
|
37237
37088
|
this.activeAssociations++;
|
|
37238
37089
|
const startMs = Date.now();
|
|
@@ -37241,7 +37092,7 @@ var DicomSender = class _DicomSender extends EventEmitter {
|
|
|
37241
37092
|
if (failure === void 0) return;
|
|
37242
37093
|
this.activeAssociations--;
|
|
37243
37094
|
this.recordFailure();
|
|
37244
|
-
this.
|
|
37095
|
+
this.config.emitters.emitSendFailed({
|
|
37245
37096
|
files: entry.files,
|
|
37246
37097
|
error: failure.error,
|
|
37247
37098
|
attempts: maxAttempts,
|
|
@@ -37251,49 +37102,27 @@ var DicomSender = class _DicomSender extends EventEmitter {
|
|
|
37251
37102
|
entry.resolve(err(failure.error));
|
|
37252
37103
|
this.drainQueue();
|
|
37253
37104
|
}
|
|
37254
|
-
/** Attempts
|
|
37105
|
+
/** Attempts the binary call up to maxAttempts times. Returns undefined on success. */
|
|
37255
37106
|
async attemptSend(entry, maxAttempts, startMs) {
|
|
37256
37107
|
let lastError;
|
|
37257
37108
|
const lastOutput = { stdout: "", stderr: "" };
|
|
37258
37109
|
for (let attempt = 0; attempt < maxAttempts; attempt++) {
|
|
37259
37110
|
if (this.isStopped) {
|
|
37260
37111
|
this.activeAssociations--;
|
|
37261
|
-
entry.resolve(err(new Error(
|
|
37112
|
+
entry.resolve(err(new Error(`${this.config.senderName}: sender stopped`)));
|
|
37262
37113
|
return void 0;
|
|
37263
37114
|
}
|
|
37264
|
-
const result = await this.
|
|
37115
|
+
const result = await this.config.executor(entry.files, entry.timeoutMs, entry.binaryParams, this.config.signal);
|
|
37265
37116
|
if (result.ok) {
|
|
37266
37117
|
this.handleSendSuccess(entry, startMs, result.value);
|
|
37267
37118
|
return void 0;
|
|
37268
37119
|
}
|
|
37269
37120
|
lastError = result.error;
|
|
37270
37121
|
if (attempt < maxAttempts - 1) {
|
|
37271
|
-
await delay2(this.retryDelayMs * (attempt + 1));
|
|
37122
|
+
await delay2(this.config.retryDelayMs * (attempt + 1));
|
|
37272
37123
|
}
|
|
37273
37124
|
}
|
|
37274
|
-
return { error: lastError ?? new Error(
|
|
37275
|
-
}
|
|
37276
|
-
/** Calls storescu with the configured options. */
|
|
37277
|
-
callStorescu(entry) {
|
|
37278
|
-
return storescu({
|
|
37279
|
-
host: this.options.host,
|
|
37280
|
-
port: this.options.port,
|
|
37281
|
-
files: [...entry.files],
|
|
37282
|
-
calledAETitle: entry.calledAETitle ?? this.options.calledAETitle,
|
|
37283
|
-
callingAETitle: entry.callingAETitle ?? this.options.callingAETitle,
|
|
37284
|
-
proposedTransferSyntax: entry.proposedTransferSyntax ?? this.options.proposedTransferSyntax,
|
|
37285
|
-
combineProposedTransferSyntaxes: entry.combineProposedTransferSyntaxes ?? this.options.combineProposedTransferSyntaxes,
|
|
37286
|
-
maxPduReceive: this.options.maxPduReceive,
|
|
37287
|
-
maxPduSend: this.options.maxPduSend,
|
|
37288
|
-
associationTimeout: this.options.associationTimeout,
|
|
37289
|
-
acseTimeout: this.options.acseTimeout,
|
|
37290
|
-
dimseTimeout: this.options.dimseTimeout,
|
|
37291
|
-
noHostnameLookup: this.options.noHostnameLookup,
|
|
37292
|
-
verbosity: this.options.verbosity,
|
|
37293
|
-
required: entry.required ?? this.options.required,
|
|
37294
|
-
timeoutMs: entry.timeoutMs,
|
|
37295
|
-
signal: this.options.signal
|
|
37296
|
-
});
|
|
37125
|
+
return { error: lastError ?? new Error(`${this.config.senderName}: send failed`), output: lastOutput };
|
|
37297
37126
|
}
|
|
37298
37127
|
/** Handles a successful send: updates state, emits event, resolves promise. */
|
|
37299
37128
|
handleSendSuccess(entry, startMs, output) {
|
|
@@ -37301,7 +37130,7 @@ var DicomSender = class _DicomSender extends EventEmitter {
|
|
|
37301
37130
|
const durationMs = Date.now() - startMs;
|
|
37302
37131
|
this.recordSuccess();
|
|
37303
37132
|
const data = { files: entry.files, fileCount: entry.files.length, durationMs, stdout: output.stdout, stderr: output.stderr };
|
|
37304
|
-
this.
|
|
37133
|
+
this.config.emitters.emitSendComplete(data);
|
|
37305
37134
|
entry.resolve(ok(data));
|
|
37306
37135
|
this.drainQueue();
|
|
37307
37136
|
}
|
|
@@ -37319,8 +37148,8 @@ var DicomSender = class _DicomSender extends EventEmitter {
|
|
|
37319
37148
|
if (this.health === SenderHealth.DOWN) {
|
|
37320
37149
|
this.health = SenderHealth.DEGRADED;
|
|
37321
37150
|
} else {
|
|
37322
|
-
this.effectiveMaxAssociations = Math.min(this.effectiveMaxAssociations * 2, this.configuredMaxAssociations);
|
|
37323
|
-
if (this.effectiveMaxAssociations >= this.configuredMaxAssociations) {
|
|
37151
|
+
this.effectiveMaxAssociations = Math.min(this.effectiveMaxAssociations * 2, this.config.configuredMaxAssociations);
|
|
37152
|
+
if (this.effectiveMaxAssociations >= this.config.configuredMaxAssociations) {
|
|
37324
37153
|
this.health = SenderHealth.HEALTHY;
|
|
37325
37154
|
}
|
|
37326
37155
|
}
|
|
@@ -37340,22 +37169,26 @@ var DicomSender = class _DicomSender extends EventEmitter {
|
|
|
37340
37169
|
this.emitHealthChanged(previousHealth);
|
|
37341
37170
|
}
|
|
37342
37171
|
} else if (this.consecutiveFailures >= DEGRADE_THRESHOLD && this.consecutiveFailures % DEGRADE_THRESHOLD === 0) {
|
|
37343
|
-
|
|
37344
|
-
|
|
37345
|
-
|
|
37172
|
+
this.degradeHealth(previousHealth);
|
|
37173
|
+
}
|
|
37174
|
+
}
|
|
37175
|
+
/** Handles HEALTHY→DEGRADED or DEGRADED→DEGRADED transitions. */
|
|
37176
|
+
degradeHealth(previousHealth) {
|
|
37177
|
+
if (this.health === SenderHealth.HEALTHY) {
|
|
37178
|
+
this.health = SenderHealth.DEGRADED;
|
|
37179
|
+
this.effectiveMaxAssociations = Math.max(1, Math.floor(this.config.configuredMaxAssociations / 2));
|
|
37180
|
+
this.emitHealthChanged(previousHealth);
|
|
37181
|
+
} else if (this.health === SenderHealth.DEGRADED) {
|
|
37182
|
+
const newMax = Math.max(1, Math.floor(this.effectiveMaxAssociations / 2));
|
|
37183
|
+
if (newMax !== this.effectiveMaxAssociations) {
|
|
37184
|
+
this.effectiveMaxAssociations = newMax;
|
|
37346
37185
|
this.emitHealthChanged(previousHealth);
|
|
37347
|
-
} else if (this.health === SenderHealth.DEGRADED) {
|
|
37348
|
-
const newMax = Math.max(1, Math.floor(this.effectiveMaxAssociations / 2));
|
|
37349
|
-
if (newMax !== this.effectiveMaxAssociations) {
|
|
37350
|
-
this.effectiveMaxAssociations = newMax;
|
|
37351
|
-
this.emitHealthChanged(previousHealth);
|
|
37352
|
-
}
|
|
37353
37186
|
}
|
|
37354
37187
|
}
|
|
37355
37188
|
}
|
|
37356
|
-
/** Emits a HEALTH_CHANGED event. */
|
|
37189
|
+
/** Emits a HEALTH_CHANGED event via the wrapper's emitter. */
|
|
37357
37190
|
emitHealthChanged(previousHealth) {
|
|
37358
|
-
this.
|
|
37191
|
+
this.config.emitters.emitHealthChanged({
|
|
37359
37192
|
previousHealth,
|
|
37360
37193
|
newHealth: this.health,
|
|
37361
37194
|
effectiveMaxAssociations: this.effectiveMaxAssociations,
|
|
@@ -37395,20 +37228,6 @@ var DicomSender = class _DicomSender extends EventEmitter {
|
|
|
37395
37228
|
check();
|
|
37396
37229
|
});
|
|
37397
37230
|
}
|
|
37398
|
-
// -----------------------------------------------------------------------
|
|
37399
|
-
// Abort signal
|
|
37400
|
-
// -----------------------------------------------------------------------
|
|
37401
|
-
/** Wires an AbortSignal to stop the sender. */
|
|
37402
|
-
wireAbortSignal(signal) {
|
|
37403
|
-
if (signal.aborted) {
|
|
37404
|
-
void this.stop();
|
|
37405
|
-
return;
|
|
37406
|
-
}
|
|
37407
|
-
this.abortHandler = () => {
|
|
37408
|
-
void this.stop();
|
|
37409
|
-
};
|
|
37410
|
-
signal.addEventListener("abort", this.abortHandler, { once: true });
|
|
37411
|
-
}
|
|
37412
37231
|
};
|
|
37413
37232
|
function delay2(ms) {
|
|
37414
37233
|
return new Promise((resolve) => {
|
|
@@ -37416,6 +37235,492 @@ function delay2(ms) {
|
|
|
37416
37235
|
});
|
|
37417
37236
|
}
|
|
37418
37237
|
|
|
37238
|
+
// src/senders/DicomSender.ts
|
|
37239
|
+
var DEFAULT_MAX_ASSOCIATIONS = 4;
|
|
37240
|
+
var DEFAULT_MAX_QUEUE_LENGTH = 1e3;
|
|
37241
|
+
var DEFAULT_MAX_RETRIES = 3;
|
|
37242
|
+
var DEFAULT_RETRY_DELAY_MS = 1e3;
|
|
37243
|
+
var DEFAULT_BUCKET_FLUSH_MS = 5e3;
|
|
37244
|
+
var DEFAULT_MAX_BUCKET_SIZE = 50;
|
|
37245
|
+
var MAX_ASSOCIATIONS_LIMIT = 64;
|
|
37246
|
+
var DicomSenderOptionsSchema = z.object({
|
|
37247
|
+
host: z.string().min(1),
|
|
37248
|
+
port: z.number().int().min(1).max(65535),
|
|
37249
|
+
calledAETitle: z.string().min(1).max(16).refine(isValidAETitle, { message: "AE Title contains invalid characters" }).optional(),
|
|
37250
|
+
callingAETitle: z.string().min(1).max(16).refine(isValidAETitle, { message: "AE Title contains invalid characters" }).optional(),
|
|
37251
|
+
mode: z.enum(["single", "multiple", "bucket"]).optional(),
|
|
37252
|
+
maxAssociations: z.number().int().min(1).max(MAX_ASSOCIATIONS_LIMIT).optional(),
|
|
37253
|
+
proposedTransferSyntax: z.union([z.enum(PROPOSED_TS_VALUES), z.array(z.enum(PROPOSED_TS_VALUES)).min(1)]).optional(),
|
|
37254
|
+
combineProposedTransferSyntaxes: z.boolean().optional(),
|
|
37255
|
+
maxQueueLength: z.number().int().min(1).optional(),
|
|
37256
|
+
timeoutMs: z.number().int().positive().optional(),
|
|
37257
|
+
maxRetries: z.number().int().min(0).optional(),
|
|
37258
|
+
retryDelayMs: z.number().int().min(0).optional(),
|
|
37259
|
+
bucketFlushMs: z.number().int().positive().optional(),
|
|
37260
|
+
maxBucketSize: z.number().int().min(1).optional(),
|
|
37261
|
+
maxPduReceive: z.number().int().min(4096).max(131072).optional(),
|
|
37262
|
+
maxPduSend: z.number().int().min(4096).max(131072).optional(),
|
|
37263
|
+
associationTimeout: z.number().int().positive().optional(),
|
|
37264
|
+
acseTimeout: z.number().int().positive().optional(),
|
|
37265
|
+
dimseTimeout: z.number().int().positive().optional(),
|
|
37266
|
+
noHostnameLookup: z.boolean().optional(),
|
|
37267
|
+
verbosity: z.enum(["verbose", "debug"]).optional(),
|
|
37268
|
+
required: z.boolean().optional(),
|
|
37269
|
+
signal: z.instanceof(AbortSignal).optional()
|
|
37270
|
+
}).strict();
|
|
37271
|
+
function createStorescuExecutor(options) {
|
|
37272
|
+
return async (files, timeoutMs, params, signal) => {
|
|
37273
|
+
const result = await storescu({
|
|
37274
|
+
host: options.host,
|
|
37275
|
+
port: options.port,
|
|
37276
|
+
files: [...files],
|
|
37277
|
+
calledAETitle: params.calledAETitle ?? options.calledAETitle,
|
|
37278
|
+
callingAETitle: params.callingAETitle ?? options.callingAETitle,
|
|
37279
|
+
proposedTransferSyntax: params.proposedTransferSyntax ?? options.proposedTransferSyntax,
|
|
37280
|
+
combineProposedTransferSyntaxes: params.combineProposedTransferSyntaxes ?? options.combineProposedTransferSyntaxes,
|
|
37281
|
+
maxPduReceive: options.maxPduReceive,
|
|
37282
|
+
maxPduSend: options.maxPduSend,
|
|
37283
|
+
associationTimeout: options.associationTimeout,
|
|
37284
|
+
acseTimeout: options.acseTimeout,
|
|
37285
|
+
dimseTimeout: options.dimseTimeout,
|
|
37286
|
+
noHostnameLookup: options.noHostnameLookup,
|
|
37287
|
+
verbosity: options.verbosity,
|
|
37288
|
+
required: params.required ?? options.required,
|
|
37289
|
+
timeoutMs,
|
|
37290
|
+
signal
|
|
37291
|
+
});
|
|
37292
|
+
if (!result.ok) return err(result.error);
|
|
37293
|
+
return ok({ stdout: result.value.stdout, stderr: result.value.stderr });
|
|
37294
|
+
};
|
|
37295
|
+
}
|
|
37296
|
+
function resolveConfig(options) {
|
|
37297
|
+
const mode = options.mode ?? "multiple";
|
|
37298
|
+
const rawMax = options.maxAssociations ?? DEFAULT_MAX_ASSOCIATIONS;
|
|
37299
|
+
return {
|
|
37300
|
+
mode,
|
|
37301
|
+
configuredMaxAssociations: mode === "single" ? 1 : rawMax,
|
|
37302
|
+
maxQueueLength: options.maxQueueLength ?? DEFAULT_MAX_QUEUE_LENGTH,
|
|
37303
|
+
defaultTimeoutMs: options.timeoutMs ?? DEFAULT_TIMEOUT_MS,
|
|
37304
|
+
defaultMaxRetries: options.maxRetries ?? DEFAULT_MAX_RETRIES,
|
|
37305
|
+
retryDelayMs: options.retryDelayMs ?? DEFAULT_RETRY_DELAY_MS,
|
|
37306
|
+
bucketFlushMs: options.bucketFlushMs ?? DEFAULT_BUCKET_FLUSH_MS,
|
|
37307
|
+
maxBucketSize: options.maxBucketSize ?? DEFAULT_MAX_BUCKET_SIZE
|
|
37308
|
+
};
|
|
37309
|
+
}
|
|
37310
|
+
var DicomSender = class _DicomSender extends EventEmitter {
|
|
37311
|
+
constructor(engine, cfg, signal) {
|
|
37312
|
+
super();
|
|
37313
|
+
__publicField(this, "engine");
|
|
37314
|
+
__publicField(this, "defaultTimeoutMs");
|
|
37315
|
+
__publicField(this, "defaultMaxRetries");
|
|
37316
|
+
__publicField(this, "signal");
|
|
37317
|
+
__publicField(this, "abortHandler");
|
|
37318
|
+
this.setMaxListeners(20);
|
|
37319
|
+
this.on("error", () => {
|
|
37320
|
+
});
|
|
37321
|
+
this.engine = engine;
|
|
37322
|
+
this.defaultTimeoutMs = cfg.defaultTimeoutMs;
|
|
37323
|
+
this.defaultMaxRetries = cfg.defaultMaxRetries;
|
|
37324
|
+
this.signal = signal;
|
|
37325
|
+
if (signal !== void 0) {
|
|
37326
|
+
this.wireAbortSignal(signal);
|
|
37327
|
+
}
|
|
37328
|
+
}
|
|
37329
|
+
// -----------------------------------------------------------------------
|
|
37330
|
+
// Public API
|
|
37331
|
+
// -----------------------------------------------------------------------
|
|
37332
|
+
/**
|
|
37333
|
+
* Creates a new DicomSender instance.
|
|
37334
|
+
*
|
|
37335
|
+
* @param options - Configuration options
|
|
37336
|
+
* @returns A Result containing the instance or a validation error
|
|
37337
|
+
*/
|
|
37338
|
+
static create(options) {
|
|
37339
|
+
const validation = DicomSenderOptionsSchema.safeParse(options);
|
|
37340
|
+
if (!validation.success) {
|
|
37341
|
+
return err(createValidationError("DicomSender", validation.error));
|
|
37342
|
+
}
|
|
37343
|
+
const cfg = resolveConfig(options);
|
|
37344
|
+
const senderRef = { current: void 0 };
|
|
37345
|
+
const engine = new SenderEngine({
|
|
37346
|
+
...cfg,
|
|
37347
|
+
executor: createStorescuExecutor(options),
|
|
37348
|
+
signal: options.signal,
|
|
37349
|
+
senderName: "DicomSender",
|
|
37350
|
+
emitters: {
|
|
37351
|
+
emitSendComplete: (data) => {
|
|
37352
|
+
senderRef.current.emit("SEND_COMPLETE", data);
|
|
37353
|
+
},
|
|
37354
|
+
emitSendFailed: (data) => {
|
|
37355
|
+
senderRef.current.emit("SEND_FAILED", data);
|
|
37356
|
+
},
|
|
37357
|
+
emitHealthChanged: (data) => {
|
|
37358
|
+
senderRef.current.emit("HEALTH_CHANGED", data);
|
|
37359
|
+
},
|
|
37360
|
+
emitBucketFlushed: (data) => {
|
|
37361
|
+
senderRef.current.emit("BUCKET_FLUSHED", data);
|
|
37362
|
+
}
|
|
37363
|
+
}
|
|
37364
|
+
});
|
|
37365
|
+
const sender = new _DicomSender(engine, cfg, options.signal);
|
|
37366
|
+
senderRef.current = sender;
|
|
37367
|
+
return ok(sender);
|
|
37368
|
+
}
|
|
37369
|
+
/**
|
|
37370
|
+
* Sends one or more DICOM files to the remote endpoint.
|
|
37371
|
+
*
|
|
37372
|
+
* In single/multiple mode, files are sent as one storescu call.
|
|
37373
|
+
* In bucket mode, files are accumulated into a bucket and flushed
|
|
37374
|
+
* when the bucket reaches maxBucketSize or the flush timer fires.
|
|
37375
|
+
*
|
|
37376
|
+
* The returned promise resolves when the files are actually sent
|
|
37377
|
+
* (not just queued). Callers can await for confirmation or
|
|
37378
|
+
* fire-and-forget with `void sender.send(files)`.
|
|
37379
|
+
*
|
|
37380
|
+
* @param files - One or more DICOM file paths
|
|
37381
|
+
* @param options - Per-send overrides
|
|
37382
|
+
* @returns A Result containing the send result or an error
|
|
37383
|
+
*/
|
|
37384
|
+
send(files, options) {
|
|
37385
|
+
const params = buildStorescuParams(options);
|
|
37386
|
+
const timeoutMs = options?.timeoutMs ?? this.defaultTimeoutMs;
|
|
37387
|
+
const maxRetries = options?.maxRetries ?? this.defaultMaxRetries;
|
|
37388
|
+
return this.engine.send(files, timeoutMs, maxRetries, params);
|
|
37389
|
+
}
|
|
37390
|
+
/**
|
|
37391
|
+
* Flushes the current bucket immediately (bucket mode only).
|
|
37392
|
+
* In single/multiple mode this is a no-op.
|
|
37393
|
+
*/
|
|
37394
|
+
flush() {
|
|
37395
|
+
this.engine.flush();
|
|
37396
|
+
}
|
|
37397
|
+
/**
|
|
37398
|
+
* Gracefully stops the sender. Rejects all queued items and
|
|
37399
|
+
* waits for active associations to complete.
|
|
37400
|
+
*/
|
|
37401
|
+
async stop() {
|
|
37402
|
+
if (this.signal !== void 0 && this.abortHandler !== void 0) {
|
|
37403
|
+
this.signal.removeEventListener("abort", this.abortHandler);
|
|
37404
|
+
}
|
|
37405
|
+
await this.engine.stop();
|
|
37406
|
+
}
|
|
37407
|
+
/** Current sender status. */
|
|
37408
|
+
get status() {
|
|
37409
|
+
return this.engine.status;
|
|
37410
|
+
}
|
|
37411
|
+
// -----------------------------------------------------------------------
|
|
37412
|
+
// Typed event listener convenience methods
|
|
37413
|
+
// -----------------------------------------------------------------------
|
|
37414
|
+
/**
|
|
37415
|
+
* Registers a typed listener for a DicomSender-specific event.
|
|
37416
|
+
*
|
|
37417
|
+
* @param event - The event name from DicomSenderEventMap
|
|
37418
|
+
* @param listener - Callback receiving typed event data
|
|
37419
|
+
* @returns this for chaining
|
|
37420
|
+
*/
|
|
37421
|
+
onEvent(event, listener) {
|
|
37422
|
+
return this.on(event, listener);
|
|
37423
|
+
}
|
|
37424
|
+
/**
|
|
37425
|
+
* Registers a listener for successful sends.
|
|
37426
|
+
*
|
|
37427
|
+
* @param listener - Callback receiving send complete data
|
|
37428
|
+
* @returns this for chaining
|
|
37429
|
+
*/
|
|
37430
|
+
onSendComplete(listener) {
|
|
37431
|
+
return this.on("SEND_COMPLETE", listener);
|
|
37432
|
+
}
|
|
37433
|
+
/**
|
|
37434
|
+
* Registers a listener for failed sends.
|
|
37435
|
+
*
|
|
37436
|
+
* @param listener - Callback receiving send failed data
|
|
37437
|
+
* @returns this for chaining
|
|
37438
|
+
*/
|
|
37439
|
+
onSendFailed(listener) {
|
|
37440
|
+
return this.on("SEND_FAILED", listener);
|
|
37441
|
+
}
|
|
37442
|
+
/**
|
|
37443
|
+
* Registers a listener for health state changes.
|
|
37444
|
+
*
|
|
37445
|
+
* @param listener - Callback receiving health change data
|
|
37446
|
+
* @returns this for chaining
|
|
37447
|
+
*/
|
|
37448
|
+
onHealthChanged(listener) {
|
|
37449
|
+
return this.on("HEALTH_CHANGED", listener);
|
|
37450
|
+
}
|
|
37451
|
+
/**
|
|
37452
|
+
* Registers a listener for bucket flushes (bucket mode only).
|
|
37453
|
+
*
|
|
37454
|
+
* @param listener - Callback receiving bucket flush data
|
|
37455
|
+
* @returns this for chaining
|
|
37456
|
+
*/
|
|
37457
|
+
onBucketFlushed(listener) {
|
|
37458
|
+
return this.on("BUCKET_FLUSHED", listener);
|
|
37459
|
+
}
|
|
37460
|
+
// -----------------------------------------------------------------------
|
|
37461
|
+
// Abort signal
|
|
37462
|
+
// -----------------------------------------------------------------------
|
|
37463
|
+
/** Wires an AbortSignal to stop the sender. */
|
|
37464
|
+
wireAbortSignal(signal) {
|
|
37465
|
+
if (signal.aborted) {
|
|
37466
|
+
void this.stop();
|
|
37467
|
+
return;
|
|
37468
|
+
}
|
|
37469
|
+
this.abortHandler = () => {
|
|
37470
|
+
void this.stop();
|
|
37471
|
+
};
|
|
37472
|
+
signal.addEventListener("abort", this.abortHandler, { once: true });
|
|
37473
|
+
}
|
|
37474
|
+
};
|
|
37475
|
+
function buildStorescuParams(options) {
|
|
37476
|
+
return {
|
|
37477
|
+
calledAETitle: options?.calledAETitle,
|
|
37478
|
+
callingAETitle: options?.callingAETitle,
|
|
37479
|
+
required: options?.required,
|
|
37480
|
+
proposedTransferSyntax: options?.proposedTransferSyntax,
|
|
37481
|
+
combineProposedTransferSyntaxes: options?.combineProposedTransferSyntaxes
|
|
37482
|
+
};
|
|
37483
|
+
}
|
|
37484
|
+
var DEFAULT_MAX_ASSOCIATIONS2 = 4;
|
|
37485
|
+
var DEFAULT_MAX_QUEUE_LENGTH2 = 1e3;
|
|
37486
|
+
var DEFAULT_MAX_RETRIES2 = 3;
|
|
37487
|
+
var DEFAULT_RETRY_DELAY_MS2 = 1e3;
|
|
37488
|
+
var DEFAULT_BUCKET_FLUSH_MS2 = 5e3;
|
|
37489
|
+
var DEFAULT_MAX_BUCKET_SIZE2 = 50;
|
|
37490
|
+
var MAX_ASSOCIATIONS_LIMIT2 = 64;
|
|
37491
|
+
var DicomSendOptionsSchema = z.object({
|
|
37492
|
+
host: z.string().min(1),
|
|
37493
|
+
port: z.number().int().min(1).max(65535),
|
|
37494
|
+
calledAETitle: z.string().min(1).max(16).refine(isValidAETitle, { message: "AE Title contains invalid characters" }).optional(),
|
|
37495
|
+
callingAETitle: z.string().min(1).max(16).refine(isValidAETitle, { message: "AE Title contains invalid characters" }).optional(),
|
|
37496
|
+
mode: z.enum(["single", "multiple", "bucket"]).optional(),
|
|
37497
|
+
maxAssociations: z.number().int().min(1).max(MAX_ASSOCIATIONS_LIMIT2).optional(),
|
|
37498
|
+
maxQueueLength: z.number().int().min(1).optional(),
|
|
37499
|
+
timeoutMs: z.number().int().positive().optional(),
|
|
37500
|
+
maxRetries: z.number().int().min(0).optional(),
|
|
37501
|
+
retryDelayMs: z.number().int().min(0).optional(),
|
|
37502
|
+
bucketFlushMs: z.number().int().positive().optional(),
|
|
37503
|
+
maxBucketSize: z.number().int().min(1).optional(),
|
|
37504
|
+
maxPduReceive: z.number().int().min(4096).max(131072).optional(),
|
|
37505
|
+
maxPduSend: z.number().int().min(4096).max(131072).optional(),
|
|
37506
|
+
associationTimeout: z.number().int().positive().optional(),
|
|
37507
|
+
acseTimeout: z.number().int().positive().optional(),
|
|
37508
|
+
dimseTimeout: z.number().int().positive().optional(),
|
|
37509
|
+
noHostnameLookup: z.boolean().optional(),
|
|
37510
|
+
verbosity: z.enum(["verbose", "debug"]).optional(),
|
|
37511
|
+
noHalt: z.boolean().optional(),
|
|
37512
|
+
noIllegalProposal: z.boolean().optional(),
|
|
37513
|
+
decompress: z.enum(["never", "lossless", "lossy"]).optional(),
|
|
37514
|
+
multiAssociations: z.boolean().optional(),
|
|
37515
|
+
noUidChecks: z.boolean().optional(),
|
|
37516
|
+
signal: z.instanceof(AbortSignal).optional()
|
|
37517
|
+
}).strict();
|
|
37518
|
+
function createDcmsendExecutor(options) {
|
|
37519
|
+
return async (files, timeoutMs, params, signal) => {
|
|
37520
|
+
const result = await dcmsend({
|
|
37521
|
+
host: options.host,
|
|
37522
|
+
port: options.port,
|
|
37523
|
+
files: [...files],
|
|
37524
|
+
calledAETitle: params.calledAETitle ?? options.calledAETitle,
|
|
37525
|
+
callingAETitle: params.callingAETitle ?? options.callingAETitle,
|
|
37526
|
+
maxPduReceive: options.maxPduReceive,
|
|
37527
|
+
maxPduSend: options.maxPduSend,
|
|
37528
|
+
associationTimeout: options.associationTimeout,
|
|
37529
|
+
acseTimeout: options.acseTimeout,
|
|
37530
|
+
dimseTimeout: options.dimseTimeout,
|
|
37531
|
+
noHostnameLookup: options.noHostnameLookup,
|
|
37532
|
+
verbosity: options.verbosity,
|
|
37533
|
+
noHalt: options.noHalt,
|
|
37534
|
+
noIllegalProposal: options.noIllegalProposal,
|
|
37535
|
+
decompress: options.decompress,
|
|
37536
|
+
multiAssociations: options.multiAssociations,
|
|
37537
|
+
noUidChecks: options.noUidChecks,
|
|
37538
|
+
timeoutMs,
|
|
37539
|
+
signal
|
|
37540
|
+
});
|
|
37541
|
+
if (!result.ok) return err(result.error);
|
|
37542
|
+
return ok({ stdout: result.value.stdout, stderr: result.value.stderr });
|
|
37543
|
+
};
|
|
37544
|
+
}
|
|
37545
|
+
function resolveConfig2(options) {
|
|
37546
|
+
const mode = options.mode ?? "multiple";
|
|
37547
|
+
const rawMax = options.maxAssociations ?? DEFAULT_MAX_ASSOCIATIONS2;
|
|
37548
|
+
return {
|
|
37549
|
+
mode,
|
|
37550
|
+
configuredMaxAssociations: mode === "single" ? 1 : rawMax,
|
|
37551
|
+
maxQueueLength: options.maxQueueLength ?? DEFAULT_MAX_QUEUE_LENGTH2,
|
|
37552
|
+
defaultTimeoutMs: options.timeoutMs ?? DEFAULT_TIMEOUT_MS,
|
|
37553
|
+
defaultMaxRetries: options.maxRetries ?? DEFAULT_MAX_RETRIES2,
|
|
37554
|
+
retryDelayMs: options.retryDelayMs ?? DEFAULT_RETRY_DELAY_MS2,
|
|
37555
|
+
bucketFlushMs: options.bucketFlushMs ?? DEFAULT_BUCKET_FLUSH_MS2,
|
|
37556
|
+
maxBucketSize: options.maxBucketSize ?? DEFAULT_MAX_BUCKET_SIZE2
|
|
37557
|
+
};
|
|
37558
|
+
}
|
|
37559
|
+
var DicomSend = class _DicomSend extends EventEmitter {
|
|
37560
|
+
constructor(engine, cfg, signal) {
|
|
37561
|
+
super();
|
|
37562
|
+
__publicField(this, "engine");
|
|
37563
|
+
__publicField(this, "defaultTimeoutMs");
|
|
37564
|
+
__publicField(this, "defaultMaxRetries");
|
|
37565
|
+
__publicField(this, "signal");
|
|
37566
|
+
__publicField(this, "abortHandler");
|
|
37567
|
+
this.setMaxListeners(20);
|
|
37568
|
+
this.on("error", () => {
|
|
37569
|
+
});
|
|
37570
|
+
this.engine = engine;
|
|
37571
|
+
this.defaultTimeoutMs = cfg.defaultTimeoutMs;
|
|
37572
|
+
this.defaultMaxRetries = cfg.defaultMaxRetries;
|
|
37573
|
+
this.signal = signal;
|
|
37574
|
+
if (signal !== void 0) {
|
|
37575
|
+
this.wireAbortSignal(signal);
|
|
37576
|
+
}
|
|
37577
|
+
}
|
|
37578
|
+
// -----------------------------------------------------------------------
|
|
37579
|
+
// Public API
|
|
37580
|
+
// -----------------------------------------------------------------------
|
|
37581
|
+
/**
|
|
37582
|
+
* Creates a new DicomSend instance.
|
|
37583
|
+
*
|
|
37584
|
+
* @param options - Configuration options
|
|
37585
|
+
* @returns A Result containing the instance or a validation error
|
|
37586
|
+
*/
|
|
37587
|
+
static create(options) {
|
|
37588
|
+
const validation = DicomSendOptionsSchema.safeParse(options);
|
|
37589
|
+
if (!validation.success) {
|
|
37590
|
+
return err(createValidationError("DicomSend", validation.error));
|
|
37591
|
+
}
|
|
37592
|
+
const cfg = resolveConfig2(options);
|
|
37593
|
+
const senderRef = { current: void 0 };
|
|
37594
|
+
const engine = new SenderEngine({
|
|
37595
|
+
...cfg,
|
|
37596
|
+
executor: createDcmsendExecutor(options),
|
|
37597
|
+
signal: options.signal,
|
|
37598
|
+
senderName: "DicomSend",
|
|
37599
|
+
emitters: {
|
|
37600
|
+
emitSendComplete: (data) => {
|
|
37601
|
+
senderRef.current.emit("SEND_COMPLETE", data);
|
|
37602
|
+
},
|
|
37603
|
+
emitSendFailed: (data) => {
|
|
37604
|
+
senderRef.current.emit("SEND_FAILED", data);
|
|
37605
|
+
},
|
|
37606
|
+
emitHealthChanged: (data) => {
|
|
37607
|
+
senderRef.current.emit("HEALTH_CHANGED", data);
|
|
37608
|
+
},
|
|
37609
|
+
emitBucketFlushed: (data) => {
|
|
37610
|
+
senderRef.current.emit("BUCKET_FLUSHED", data);
|
|
37611
|
+
}
|
|
37612
|
+
}
|
|
37613
|
+
});
|
|
37614
|
+
const sender = new _DicomSend(engine, cfg, options.signal);
|
|
37615
|
+
senderRef.current = sender;
|
|
37616
|
+
return ok(sender);
|
|
37617
|
+
}
|
|
37618
|
+
/**
|
|
37619
|
+
* Sends one or more DICOM files to the remote endpoint.
|
|
37620
|
+
*
|
|
37621
|
+
* In single/multiple mode, files are sent as one dcmsend call.
|
|
37622
|
+
* In bucket mode, files are accumulated into a bucket and flushed
|
|
37623
|
+
* when the bucket reaches maxBucketSize or the flush timer fires.
|
|
37624
|
+
*
|
|
37625
|
+
* @param files - One or more DICOM file paths
|
|
37626
|
+
* @param options - Per-send overrides
|
|
37627
|
+
* @returns A Result containing the send result or an error
|
|
37628
|
+
*/
|
|
37629
|
+
send(files, options) {
|
|
37630
|
+
const params = {
|
|
37631
|
+
calledAETitle: options?.calledAETitle,
|
|
37632
|
+
callingAETitle: options?.callingAETitle
|
|
37633
|
+
};
|
|
37634
|
+
const timeoutMs = options?.timeoutMs ?? this.defaultTimeoutMs;
|
|
37635
|
+
const maxRetries = options?.maxRetries ?? this.defaultMaxRetries;
|
|
37636
|
+
return this.engine.send(files, timeoutMs, maxRetries, params);
|
|
37637
|
+
}
|
|
37638
|
+
/**
|
|
37639
|
+
* Flushes the current bucket immediately (bucket mode only).
|
|
37640
|
+
* In single/multiple mode this is a no-op.
|
|
37641
|
+
*/
|
|
37642
|
+
flush() {
|
|
37643
|
+
this.engine.flush();
|
|
37644
|
+
}
|
|
37645
|
+
/**
|
|
37646
|
+
* Gracefully stops the sender. Rejects all queued items and
|
|
37647
|
+
* waits for active associations to complete.
|
|
37648
|
+
*/
|
|
37649
|
+
async stop() {
|
|
37650
|
+
if (this.signal !== void 0 && this.abortHandler !== void 0) {
|
|
37651
|
+
this.signal.removeEventListener("abort", this.abortHandler);
|
|
37652
|
+
}
|
|
37653
|
+
await this.engine.stop();
|
|
37654
|
+
}
|
|
37655
|
+
/** Current sender status. */
|
|
37656
|
+
get status() {
|
|
37657
|
+
return this.engine.status;
|
|
37658
|
+
}
|
|
37659
|
+
// -----------------------------------------------------------------------
|
|
37660
|
+
// Typed event listener convenience methods
|
|
37661
|
+
// -----------------------------------------------------------------------
|
|
37662
|
+
/**
|
|
37663
|
+
* Registers a typed listener for a DicomSend-specific event.
|
|
37664
|
+
*
|
|
37665
|
+
* @param event - The event name from DicomSendEventMap
|
|
37666
|
+
* @param listener - Callback receiving typed event data
|
|
37667
|
+
* @returns this for chaining
|
|
37668
|
+
*/
|
|
37669
|
+
onEvent(event, listener) {
|
|
37670
|
+
return this.on(event, listener);
|
|
37671
|
+
}
|
|
37672
|
+
/**
|
|
37673
|
+
* Registers a listener for successful sends.
|
|
37674
|
+
*
|
|
37675
|
+
* @param listener - Callback receiving send complete data
|
|
37676
|
+
* @returns this for chaining
|
|
37677
|
+
*/
|
|
37678
|
+
onSendComplete(listener) {
|
|
37679
|
+
return this.on("SEND_COMPLETE", listener);
|
|
37680
|
+
}
|
|
37681
|
+
/**
|
|
37682
|
+
* Registers a listener for failed sends.
|
|
37683
|
+
*
|
|
37684
|
+
* @param listener - Callback receiving send failed data
|
|
37685
|
+
* @returns this for chaining
|
|
37686
|
+
*/
|
|
37687
|
+
onSendFailed(listener) {
|
|
37688
|
+
return this.on("SEND_FAILED", listener);
|
|
37689
|
+
}
|
|
37690
|
+
/**
|
|
37691
|
+
* Registers a listener for health state changes.
|
|
37692
|
+
*
|
|
37693
|
+
* @param listener - Callback receiving health change data
|
|
37694
|
+
* @returns this for chaining
|
|
37695
|
+
*/
|
|
37696
|
+
onHealthChanged(listener) {
|
|
37697
|
+
return this.on("HEALTH_CHANGED", listener);
|
|
37698
|
+
}
|
|
37699
|
+
/**
|
|
37700
|
+
* Registers a listener for bucket flushes (bucket mode only).
|
|
37701
|
+
*
|
|
37702
|
+
* @param listener - Callback receiving bucket flush data
|
|
37703
|
+
* @returns this for chaining
|
|
37704
|
+
*/
|
|
37705
|
+
onBucketFlushed(listener) {
|
|
37706
|
+
return this.on("BUCKET_FLUSHED", listener);
|
|
37707
|
+
}
|
|
37708
|
+
// -----------------------------------------------------------------------
|
|
37709
|
+
// Abort signal
|
|
37710
|
+
// -----------------------------------------------------------------------
|
|
37711
|
+
/** Wires an AbortSignal to stop the sender. */
|
|
37712
|
+
wireAbortSignal(signal) {
|
|
37713
|
+
if (signal.aborted) {
|
|
37714
|
+
void this.stop();
|
|
37715
|
+
return;
|
|
37716
|
+
}
|
|
37717
|
+
this.abortHandler = () => {
|
|
37718
|
+
void this.stop();
|
|
37719
|
+
};
|
|
37720
|
+
signal.addEventListener("abort", this.abortHandler, { once: true });
|
|
37721
|
+
}
|
|
37722
|
+
};
|
|
37723
|
+
|
|
37419
37724
|
// src/pacs/types.ts
|
|
37420
37725
|
var QueryLevel = {
|
|
37421
37726
|
STUDY: "STUDY",
|
|
@@ -37965,7 +38270,7 @@ var DEFAULT_CONFIG = {
|
|
|
37965
38270
|
signal: void 0,
|
|
37966
38271
|
onRetry: void 0
|
|
37967
38272
|
};
|
|
37968
|
-
function
|
|
38273
|
+
function resolveConfig3(opts) {
|
|
37969
38274
|
if (!opts) return DEFAULT_CONFIG;
|
|
37970
38275
|
return {
|
|
37971
38276
|
...DEFAULT_CONFIG,
|
|
@@ -38010,7 +38315,7 @@ function shouldBreakAfterFailure(attempt, lastError, config) {
|
|
|
38010
38315
|
return false;
|
|
38011
38316
|
}
|
|
38012
38317
|
async function retry(operation, options) {
|
|
38013
|
-
const config =
|
|
38318
|
+
const config = resolveConfig3(options);
|
|
38014
38319
|
let lastResult = err(new Error("No attempts executed"));
|
|
38015
38320
|
for (let attempt = 0; attempt < config.maxAttempts; attempt += 1) {
|
|
38016
38321
|
lastResult = await operation();
|
|
@@ -38024,6 +38329,6 @@ async function retry(operation, options) {
|
|
|
38024
38329
|
return lastResult;
|
|
38025
38330
|
}
|
|
38026
38331
|
|
|
38027
|
-
export { AETitleSchema, AssociationTracker, ChangeSet, ColorConversion, DCMPRSCP_FATAL_EVENTS, DCMPRSCP_PATTERNS, DCMPSRCV_FATAL_EVENTS, DCMPSRCV_PATTERNS, DCMQRSCP_FATAL_EVENTS, DCMQRSCP_PATTERNS, DCMRECV_FATAL_EVENTS, DCMRECV_PATTERNS, DEFAULT_BLOCK_TIMEOUT_MS, DEFAULT_DICOM_PORT, DEFAULT_DRAIN_TIMEOUT_MS, DEFAULT_PARSE_CONCURRENCY, DEFAULT_START_TIMEOUT_MS, DEFAULT_TIMEOUT_MS, Dcm2pnmOutputFormat, Dcm2xmlCharset, DcmQRSCP, DcmdumpFormat, Dcmj2pnmOutputFormat, DcmprsCP, DcmprscpEvent, Dcmpsrcv, DcmpsrcvEvent, DcmqrscpEvent, Dcmrecv, DcmrecvEvent, DcmtkProcess, DicomDataset, DicomInstance, DicomReceiver, DicomSender, DicomTagPathSchema, DicomTagSchema, FilenameMode, GetQueryModel, Img2dcmInputFormat, JplsColorConversion, LineParser, LutType, MAX_BLOCK_LINES, MAX_CHANGESET_OPERATIONS, MAX_EVENT_PATTERNS, MAX_TRAVERSAL_DEPTH, MoveQueryModel, PDU_SIZE, PacsClient, PortSchema, PreferredTransferSyntax, ProcessState, ProposedTransferSyntax, QueryLevel, QueryModel, REQUIRED_BINARIES, RetrieveMode, SOP_CLASSES, STORESCP_FATAL_EVENTS, STORESCP_PATTERNS, SenderHealth, SenderMode, StorageMode, StoreSCP, StoreSCPPreset, StorescpEvent, SubdirectoryMode, TransferSyntax, UIDSchema, UNIX_SEARCH_PATHS, VR, VR_CATEGORY, VR_CATEGORY_NAME, VR_META, WINDOWS_SEARCH_PATHS, WLMSCPFS_FATAL_EVENTS, WLMSCPFS_PATTERNS, Wlmscpfs, WlmscpfsEvent, assertUnreachable, batch, cda2dcm, clearDcmtkPathCache, createAETitle, createDicomFilePath, createDicomTag, createDicomTagPath, createPort, createSOPClassUID, createTransferSyntaxUID, dcm2cda, dcm2json, dcm2pdf, dcm2pnm, dcm2xml, dcmcjpeg, dcmcjpls, dcmconv, dcmcrle, dcmdecap, dcmdjpeg, dcmdjpls, dcmdrle, dcmdspfn, dcmdump, dcmencap, dcmftest, dcmgpdir, dcmj2pnm, dcmmkcrv, dcmmkdir, dcmmklut, dcmodify, dcmp2pgm, dcmprscu, dcmpschk, dcmpsmk, dcmpsprt, dcmqridx, dcmquant, dcmscale, dcmsend, dcod2lum, dconvlum, drtdump, dsr2xml, dsrdump, dump2dcm, echoscu, err, execCommand, findDcmtkPath, findscu, getVRCategory, getscu, img2dcm, isBinaryVR, isNumericVR, isStringVR, json2dcm, lookupTag, lookupTagByKeyword, lookupTagByName, mapResult, movescu, ok, parseAETitle, parseDicomTag, parseDicomTagPath, parsePort, parseSOPClassUID, parseTransferSyntaxUID, pdf2dcm, retry, segmentsToModifyPath, segmentsToString, sopClassNameFromUID, spawnCommand, stl2dcm, storescu, tag, tagPathToSegments, termscu, walkTags, xml2dcm, xml2dsr, xmlToJson };
|
|
38332
|
+
export { AETitleSchema, AssociationTracker, ChangeSet, ColorConversion, DCMPRSCP_FATAL_EVENTS, DCMPRSCP_PATTERNS, DCMPSRCV_FATAL_EVENTS, DCMPSRCV_PATTERNS, DCMQRSCP_FATAL_EVENTS, DCMQRSCP_PATTERNS, DCMRECV_FATAL_EVENTS, DCMRECV_PATTERNS, DEFAULT_BLOCK_TIMEOUT_MS, DEFAULT_DICOM_PORT, DEFAULT_DRAIN_TIMEOUT_MS, DEFAULT_PARSE_CONCURRENCY, DEFAULT_START_TIMEOUT_MS, DEFAULT_TIMEOUT_MS, Dcm2pnmOutputFormat, Dcm2xmlCharset, DcmQRSCP, DcmdumpFormat, Dcmj2pnmOutputFormat, DcmprsCP, DcmprscpEvent, Dcmpsrcv, DcmpsrcvEvent, DcmqrscpEvent, Dcmrecv, DcmrecvEvent, DcmtkProcess, DicomDataset, DicomInstance, DicomReceiver, DicomSend, DicomSender, DicomTagPathSchema, DicomTagSchema, FilenameMode, GetQueryModel, Img2dcmInputFormat, JplsColorConversion, LineParser, LutType, MAX_BLOCK_LINES, MAX_CHANGESET_OPERATIONS, MAX_EVENT_PATTERNS, MAX_TRAVERSAL_DEPTH, MoveQueryModel, PDU_SIZE, PacsClient, PortSchema, PreferredTransferSyntax, ProcessState, ProposedTransferSyntax, QueryLevel, QueryModel, REQUIRED_BINARIES, RetrieveMode, SOP_CLASSES, STORESCP_FATAL_EVENTS, STORESCP_PATTERNS, SenderHealth, SenderMode, StorageMode, StoreSCP, StoreSCPPreset, StorescpEvent, SubdirectoryMode, TransferSyntax, UIDSchema, UNIX_SEARCH_PATHS, VR, VR_CATEGORY, VR_CATEGORY_NAME, VR_META, WINDOWS_SEARCH_PATHS, WLMSCPFS_FATAL_EVENTS, WLMSCPFS_PATTERNS, Wlmscpfs, WlmscpfsEvent, assertUnreachable, batch, cda2dcm, clearDcmtkPathCache, createAETitle, createDicomFilePath, createDicomTag, createDicomTagPath, createPort, createSOPClassUID, createTransferSyntaxUID, dcm2cda, dcm2json, dcm2pdf, dcm2pnm, dcm2xml, dcmcjpeg, dcmcjpls, dcmconv, dcmcrle, dcmdecap, dcmdjpeg, dcmdjpls, dcmdrle, dcmdspfn, dcmdump, dcmencap, dcmftest, dcmgpdir, dcmj2pnm, dcmmkcrv, dcmmkdir, dcmmklut, dcmodify, dcmp2pgm, dcmprscu, dcmpschk, dcmpsmk, dcmpsprt, dcmqridx, dcmquant, dcmscale, dcmsend, dcod2lum, dconvlum, drtdump, dsr2xml, dsrdump, dump2dcm, echoscu, err, execCommand, findDcmtkPath, findscu, getVRCategory, getscu, img2dcm, isBinaryVR, isNumericVR, isStringVR, json2dcm, lookupTag, lookupTagByKeyword, lookupTagByName, mapResult, movescu, ok, parseAETitle, parseDicomTag, parseDicomTagPath, parsePort, parseSOPClassUID, parseTransferSyntaxUID, pdf2dcm, retry, segmentsToModifyPath, segmentsToString, sopClassNameFromUID, spawnCommand, stl2dcm, storescu, tag, tagPathToSegments, termscu, walkTags, xml2dcm, xml2dsr, xmlToJson };
|
|
38028
38333
|
//# sourceMappingURL=index.js.map
|
|
38029
38334
|
//# sourceMappingURL=index.js.map
|