module planning; import std.stdio; import std.file; import std.path; import std.algorithm; import std.array; import std.string; import omspec_ipc; private int MAX_FILES_PER_CHUNK = 2; private string[] ALLOWED_EXTENSIONS = ['.txt', '.tif', '.npy']; private int MIN_BANDS_NEEDED = 4; /** * Scans the target and builds the Chunk array. */ public DatasetChunk[] generate_plan(string root_path, int maxDepth) { DatasetChunk[] total_plan; Dataset[] found_dirs; scan_directory_recursive(root_path, 0, maxDepth, found_dirs); foreach(ds; found_dirs) { DatasetChunk chunk; chunk.directory = ds.path; chunk.file_count = 0; auto entries = dirEntries(ds.path, SpanMode.shallow); // D-style 'Set': bool[string] bool[string] found_bases; MultiSpectralImageGroup[string] found_multispectral_images; foreach(entry; entries) { if (!entry.isFile) continue; // Use std.path functions string extn = extension(entry.name); string base = baseName(entry.name, extn); // Get filename without path or extension if (!ALLOWED_EXTENSIONS.canFind(extn.toLower())) { writeln("extension ", extn, " not allowed"); continue; } auto img_base_idx = base.lastIndexOf('_'); // Check if underscore exists AND isn't at the very start/end if (img_base_idx <= 0 || img_base_idx >= (base.length - 1)) { // writefln("File %s skipped: naming convention mismatch", base); continue; } string img_base = base[0 .. img_base_idx]; string img_band = base[img_base_idx+1 .. $].toLower(); // Check 'Set' existence using 'in' operator if (img_base !in found_bases) { found_bases[img_base] = true; // Fixed: No named arguments in D. Initialize then set. MultiSpectralImageGroup group; group.directory = chunk.directory; found_multispectral_images[img_base] = group; } found_multispectral_images[img_base].bands ~= img_band; found_multispectral_images[img_base].fname[img_band] = entry.name; } // Iterate through Associative Array foreach (img_base; found_multispectral_images.keys) { auto multispectral_img = found_multispectral_images[img_base]; if (multispectral_img.bands.length < MIN_BANDS_NEEDED) { writefln("[WARNING]: %s in %s has only %d bands. Skipping.", img_base, chunk.directory, multispectral_img.bands.length); continue; } chunk.images ~= multispectral_img; chunk.file_count++; } if (chunk.file_count > 0) total_plan ~= chunk; } return total_plan; } /** * Recursive discovery of folders containing files */ private void scan_directory_recursive(string currentPath, int currentDepth, int maxDepth, ref Dataset[] datasets) { if (currentDepth > maxDepth) return; if (!exists(currentPath) || !isDir(currentPath)) return; auto entries = dirEntries(currentPath, SpanMode.shallow).array; long fCount = entries.filter!(e => e.isFile).count; if (fCount > 0) { datasets ~= Dataset(currentPath, fCount); } foreach (entry; entries) { if (entry.isDir) { scan_directory_recursive(entry.name, currentDepth + 1, maxDepth, datasets); } } }