06.2026
This commit is contained in:
@@ -0,0 +1,419 @@
|
||||
package sparseviewer.io;
|
||||
|
||||
import sparseviewer.mda.MdaDetector;
|
||||
import sparseviewer.mda.MdaPositioner;
|
||||
import sparseviewer.mda.MdaScan;
|
||||
import sparseviewer.mda.XdrInput;
|
||||
import sparseviewer.model.ScanDataset;
|
||||
import sparseviewer.mda.MdaFileData;
|
||||
|
||||
import ij.IJ;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class MdaReader {
|
||||
|
||||
public static ScanDataset readFiles(List<File> files) throws IOException {
|
||||
if (files == null || files.isEmpty()) {
|
||||
throw new IOException("No MDA file selected.");
|
||||
}
|
||||
|
||||
if (files.size() > 1) {
|
||||
throw new IOException("MDA support currently expects a single file.");
|
||||
}
|
||||
|
||||
File file = files.get(0);
|
||||
|
||||
MdaFileData mda = readMdaFile(file);
|
||||
|
||||
if (mda.rank == 1) {
|
||||
return toScanDatasetRank1(mda, file);
|
||||
}
|
||||
|
||||
if (mda.rank == 2) {
|
||||
return toScanDatasetRank2(mda, file);
|
||||
}
|
||||
|
||||
throw new IOException("Only MDA rank 1 and rank 2 are supported currently. File rank = " + mda.rank);
|
||||
}
|
||||
private static MdaFileData readMdaFile(File file) throws IOException {
|
||||
XdrInput in = new XdrInput(file);
|
||||
|
||||
try {
|
||||
MdaFileData data = new MdaFileData();
|
||||
|
||||
data.version = in.readFloat();
|
||||
|
||||
if (Math.abs(data.version - 1.3f) > 0.02f
|
||||
&& Math.abs(data.version - 1.4f) > 0.02f) {
|
||||
throw new IOException("Unsupported MDA version: " + data.version);
|
||||
}
|
||||
|
||||
data.scanNumber = in.readInt();
|
||||
data.rank = in.readInt();
|
||||
|
||||
if (data.rank < 1 || data.rank > 4) {
|
||||
throw new IOException("Invalid MDA rank: " + data.rank);
|
||||
}
|
||||
|
||||
data.dimensions = in.readIntArray(data.rank);
|
||||
data.isRegular = in.readInt();
|
||||
data.pExtra = in.readInt();
|
||||
|
||||
long mainScanOffset = in.getFilePointer();
|
||||
|
||||
data.mainScan = readScan(in, mainScanOffset);
|
||||
|
||||
if (data.rank == 2) {
|
||||
readRank2InnerScans(in, data);
|
||||
}
|
||||
|
||||
IJ.log("MDA loaded: " + file.getName()
|
||||
+ ", version=" + data.version
|
||||
+ ", rank=" + data.rank
|
||||
+ ", scanNumber=" + data.scanNumber);
|
||||
|
||||
return data;
|
||||
|
||||
} finally {
|
||||
in.close();
|
||||
}
|
||||
}
|
||||
private static void readRank2InnerScans(XdrInput in, MdaFileData data) throws IOException {
|
||||
MdaScan outer = data.mainScan;
|
||||
|
||||
if (outer.lowerScanOffsets == null) {
|
||||
throw new IOException("Rank 2 MDA file has no lower scan offsets.");
|
||||
}
|
||||
|
||||
int nOuter = Math.min(outer.currPt, outer.lowerScanOffsets.length);
|
||||
|
||||
for (int i = 0; i < nOuter; i++) {
|
||||
int offset = outer.lowerScanOffsets[i];
|
||||
|
||||
if (offset <= 0) {
|
||||
data.innerScans.add(null);
|
||||
continue;
|
||||
}
|
||||
|
||||
MdaScan inner = readScan(in, offset);
|
||||
data.innerScans.add(inner);
|
||||
}
|
||||
}
|
||||
private static MdaScan readScan(XdrInput in, long offset) throws IOException {
|
||||
in.seek(offset);
|
||||
|
||||
MdaScan scan = new MdaScan();
|
||||
|
||||
scan.rank = in.readInt();
|
||||
|
||||
if (scan.rank < 0 || scan.rank > 20) {
|
||||
throw new IOException("Invalid scan rank at offset " + offset + ": " + scan.rank);
|
||||
}
|
||||
|
||||
scan.npts = in.readInt();
|
||||
scan.currPt = in.readInt();
|
||||
|
||||
if (scan.rank > 1) {
|
||||
scan.lowerScanOffsets = in.readIntArray(scan.npts);
|
||||
}
|
||||
|
||||
scan.name = in.readStringWithLeadingLength();
|
||||
scan.time = in.readStringWithLeadingLength();
|
||||
|
||||
scan.np = in.readInt();
|
||||
scan.nd = in.readInt();
|
||||
scan.nt = in.readInt();
|
||||
|
||||
for (int i = 0; i < scan.np; i++) {
|
||||
MdaPositioner p = new MdaPositioner();
|
||||
|
||||
p.number = in.readInt();
|
||||
p.fieldName = posName(p.number);
|
||||
|
||||
p.name = in.readStringWithLeadingLength();
|
||||
p.desc = in.readStringWithLeadingLength();
|
||||
p.stepMode = in.readStringWithLeadingLength();
|
||||
p.unit = in.readStringWithLeadingLength();
|
||||
|
||||
p.readbackName = in.readStringWithLeadingLength();
|
||||
p.readbackDesc = in.readStringWithLeadingLength();
|
||||
p.readbackUnit = in.readStringWithLeadingLength();
|
||||
|
||||
scan.positioners.add(p);
|
||||
}
|
||||
|
||||
for (int i = 0; i < scan.nd; i++) {
|
||||
MdaDetector d = new MdaDetector();
|
||||
|
||||
d.number = in.readInt();
|
||||
d.fieldName = detName(d.number);
|
||||
|
||||
d.name = in.readStringWithLeadingLength();
|
||||
d.desc = in.readStringWithLeadingLength();
|
||||
d.unit = in.readStringWithLeadingLength();
|
||||
|
||||
scan.detectors.add(d);
|
||||
}
|
||||
|
||||
// Triggers are not needed for ScanDataset, but must be consumed.
|
||||
for (int i = 0; i < scan.nt; i++) {
|
||||
in.readInt(); // trigger number
|
||||
in.readStringWithLeadingLength(); // trigger name
|
||||
in.readFloat(); // command
|
||||
}
|
||||
|
||||
readScanData(in, scan);
|
||||
|
||||
return scan;
|
||||
}
|
||||
private static void readScanData(XdrInput in, MdaScan scan) throws IOException {
|
||||
for (int p = 0; p < scan.np; p++) {
|
||||
double[] values = new double[scan.npts];
|
||||
|
||||
for (int i = 0; i < scan.npts; i++) {
|
||||
values[i] = in.readDouble();
|
||||
}
|
||||
|
||||
scan.positioners.get(p).data = values;
|
||||
}
|
||||
|
||||
for (int d = 0; d < scan.nd; d++) {
|
||||
float[] values = new float[scan.npts];
|
||||
|
||||
for (int i = 0; i < scan.npts; i++) {
|
||||
values[i] = in.readFloat();
|
||||
}
|
||||
|
||||
scan.detectors.get(d).data = values;
|
||||
}
|
||||
}
|
||||
private static ScanDataset toScanDatasetRank1(MdaFileData mda, File file) {
|
||||
MdaScan scan = mda.mainScan;
|
||||
|
||||
int rowCount = scan.currPt;
|
||||
int channelCount = scan.np + scan.nd;
|
||||
|
||||
String[] channelNames = new String[channelCount];
|
||||
double[][] columns = new double[channelCount][rowCount];
|
||||
int[] fileIndex = new int[rowCount];
|
||||
|
||||
int c = 0;
|
||||
|
||||
for (int p = 0; p < scan.np; p++) {
|
||||
MdaPositioner pos = scan.positioners.get(p);
|
||||
channelNames[c] = makePositionerName("rank1", pos);
|
||||
|
||||
for (int i = 0; i < rowCount; i++) {
|
||||
columns[c][i] = safeDouble(pos.data, i);
|
||||
}
|
||||
|
||||
c++;
|
||||
}
|
||||
|
||||
for (int d = 0; d < scan.nd; d++) {
|
||||
MdaDetector det = scan.detectors.get(d);
|
||||
channelNames[c] = makeDetectorName("rank1", det);
|
||||
|
||||
for (int i = 0; i < rowCount; i++) {
|
||||
columns[c][i] = safeFloat(det.data, i);
|
||||
}
|
||||
|
||||
c++;
|
||||
}
|
||||
|
||||
for (int i = 0; i < rowCount; i++) {
|
||||
fileIndex[i] = 0;
|
||||
}
|
||||
|
||||
List<File> sourceFiles = new ArrayList<File>();
|
||||
sourceFiles.add(file);
|
||||
|
||||
IJ.log("MDA rank 1 converted to ScanDataset: "
|
||||
+ rowCount + " rows, " + channelCount + " channels.");
|
||||
|
||||
return new ScanDataset(channelNames, columns, rowCount, fileIndex, sourceFiles);
|
||||
}
|
||||
private static ScanDataset toScanDatasetRank2(MdaFileData mda, File file) throws IOException {
|
||||
MdaScan outer = mda.mainScan;
|
||||
|
||||
MdaScan firstInner = findFirstInnerScan(mda);
|
||||
|
||||
if (firstInner == null) {
|
||||
throw new IOException("Rank 2 MDA file contains no readable inner scan.");
|
||||
}
|
||||
|
||||
int rowCount = 0;
|
||||
|
||||
for (int j = 0; j < mda.innerScans.size(); j++) {
|
||||
MdaScan inner = mda.innerScans.get(j);
|
||||
|
||||
if (inner != null) {
|
||||
rowCount += inner.currPt;
|
||||
}
|
||||
}
|
||||
|
||||
int channelCount = outer.np + outer.nd + firstInner.np + firstInner.nd;
|
||||
|
||||
String[] channelNames = new String[channelCount];
|
||||
double[][] columns = new double[channelCount][rowCount];
|
||||
int[] fileIndex = new int[rowCount];
|
||||
|
||||
int c = 0;
|
||||
|
||||
for (int p = 0; p < outer.np; p++) {
|
||||
channelNames[c++] = makePositionerName("outer", outer.positioners.get(p));
|
||||
}
|
||||
|
||||
for (int d = 0; d < outer.nd; d++) {
|
||||
channelNames[c++] = makeDetectorName("outer", outer.detectors.get(d));
|
||||
}
|
||||
|
||||
for (int p = 0; p < firstInner.np; p++) {
|
||||
channelNames[c++] = makePositionerName("inner", firstInner.positioners.get(p));
|
||||
}
|
||||
|
||||
for (int d = 0; d < firstInner.nd; d++) {
|
||||
channelNames[c++] = makeDetectorName("inner", firstInner.detectors.get(d));
|
||||
}
|
||||
|
||||
int row = 0;
|
||||
|
||||
for (int j = 0; j < mda.innerScans.size(); j++) {
|
||||
MdaScan inner = mda.innerScans.get(j);
|
||||
|
||||
if (inner == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
int innerRows = inner.currPt;
|
||||
|
||||
for (int k = 0; k < innerRows; k++) {
|
||||
c = 0;
|
||||
|
||||
for (int p = 0; p < outer.np; p++) {
|
||||
columns[c++][row] = safeDouble(outer.positioners.get(p).data, j);
|
||||
}
|
||||
|
||||
for (int d = 0; d < outer.nd; d++) {
|
||||
columns[c++][row] = safeFloat(outer.detectors.get(d).data, j);
|
||||
}
|
||||
|
||||
for (int p = 0; p < firstInner.np; p++) {
|
||||
if (p < inner.positioners.size()) {
|
||||
columns[c++][row] = safeDouble(inner.positioners.get(p).data, k);
|
||||
} else {
|
||||
columns[c++][row] = Double.NaN;
|
||||
}
|
||||
}
|
||||
|
||||
for (int d = 0; d < firstInner.nd; d++) {
|
||||
if (d < inner.detectors.size()) {
|
||||
columns[c++][row] = safeFloat(inner.detectors.get(d).data, k);
|
||||
} else {
|
||||
columns[c++][row] = Double.NaN;
|
||||
}
|
||||
}
|
||||
|
||||
fileIndex[row] = 0;
|
||||
row++;
|
||||
}
|
||||
}
|
||||
|
||||
List<File> sourceFiles = new ArrayList<File>();
|
||||
sourceFiles.add(file);
|
||||
|
||||
IJ.log("MDA rank 2 converted to ScanDataset: "
|
||||
+ rowCount + " rows, " + channelCount + " channels.");
|
||||
|
||||
return new ScanDataset(channelNames, columns, rowCount, fileIndex, sourceFiles);
|
||||
}
|
||||
private static MdaScan findFirstInnerScan(MdaFileData mda) {
|
||||
for (int i = 0; i < mda.innerScans.size(); i++) {
|
||||
MdaScan scan = mda.innerScans.get(i);
|
||||
|
||||
if (scan != null) {
|
||||
return scan;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
private static String makePositionerName(String prefix, MdaPositioner p) {
|
||||
String pv = clean(p.name);
|
||||
String desc = clean(p.desc);
|
||||
|
||||
if (pv.length() > 0 && desc.length() > 0) {
|
||||
return prefix + "/" + p.fieldName + "/" + pv + " {" + desc + "}";
|
||||
}
|
||||
|
||||
if (pv.length() > 0) {
|
||||
return prefix + "/" + p.fieldName + "/" + pv;
|
||||
}
|
||||
|
||||
if (desc.length() > 0) {
|
||||
return prefix + "/" + p.fieldName + "/{" + desc + "}";
|
||||
}
|
||||
|
||||
return prefix + "/" + p.fieldName;
|
||||
}
|
||||
private static String makeDetectorName(String prefix, MdaDetector d) {
|
||||
String pv = clean(d.name);
|
||||
String desc = clean(d.desc);
|
||||
|
||||
if (pv.length() > 0 && desc.length() > 0) {
|
||||
return prefix + "/" + d.fieldName + "/" + pv + " {" + desc + "}";
|
||||
}
|
||||
|
||||
if (pv.length() > 0) {
|
||||
return prefix + "/" + d.fieldName + "/" + pv;
|
||||
}
|
||||
|
||||
if (desc.length() > 0) {
|
||||
return prefix + "/" + d.fieldName + "/{" + desc + "}";
|
||||
}
|
||||
|
||||
return prefix + "/" + d.fieldName;
|
||||
}
|
||||
private static String clean(String s) {
|
||||
return s == null ? "" : s.trim();
|
||||
}
|
||||
private static double safeDouble(double[] data, int index) {
|
||||
if (data == null || index < 0 || index >= data.length) {
|
||||
return Double.NaN;
|
||||
}
|
||||
|
||||
return data[index];
|
||||
}
|
||||
private static double safeFloat(float[] data, int index) {
|
||||
if (data == null || index < 0 || index >= data.length) {
|
||||
return Double.NaN;
|
||||
}
|
||||
|
||||
return data[index];
|
||||
}
|
||||
private static String posName(int i) {
|
||||
if (i < 4) {
|
||||
return "P" + (i + 1);
|
||||
}
|
||||
|
||||
return "?";
|
||||
}
|
||||
private static String detName(int i) {
|
||||
if (i < 100) {
|
||||
int n = i + 1;
|
||||
|
||||
if (n < 10) {
|
||||
return "D0" + n;
|
||||
}
|
||||
|
||||
return "D" + n;
|
||||
}
|
||||
|
||||
return "?";
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user