IPP Software Navigation Tools IPP Links Communication Pan-STARRS Links

Ignore:
Timestamp:
Mar 5, 2009, 11:13:29 AM (17 years ago)
Author:
beaumont
Message:

Finished ppStack visualization. Unified the way in which visualizations are invoked, plotting windows are closed, etc

Location:
branches/cnb_branches/cnb_branch_20090215
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • branches/cnb_branches/cnb_branch_20090215

  • branches/cnb_branches/cnb_branch_20090215/psModules/src/concepts/pmConcepts.c

    r20951 r23197  
    1919#include "pmConceptsWrite.h"
    2020#include "pmConceptsStandard.h"
    21 #include "pmConceptsUpdate.h"
    2221
    2322static bool conceptsInitialised = false;// Have concepts been read?
     
    4948
    5049pmConceptSpec *pmConceptSpecAlloc(psMetadataItem *blank, pmConceptParseFunc parse,
    51                                   pmConceptFormatFunc format, bool required)
     50                                  pmConceptFormatFunc format, pmConceptCopyFunc copy,
     51                                  bool required)
    5252{
    5353    pmConceptSpec *spec = psAlloc(sizeof(pmConceptSpec));
     
    5757    spec->parse = parse;
    5858    spec->format = format;
     59    spec->copy = copy;
    5960    spec->required = required;
    6061
     
    8687}
    8788
     89psMetadata *pmConceptsSpecs(pmFPALevel level)
     90{
     91    if (!conceptsInitialised) {
     92        pmConceptsInit();
     93    }
     94
     95    // Get the appropriate concepts
     96    psMetadata *concepts = conceptsFromLevel(level); // Metadata of concepts specs
     97    if (!concepts) {
     98        psError(PS_ERR_BAD_PARAMETER_VALUE, true, "Invalid concept level provided: %d\n", level);
     99        return NULL;
     100    }
     101
     102    return concepts;
     103}
     104
    88105bool pmConceptGetRequired(const char *name, pmFPALevel level)
    89106{
     
    129146
    130147bool pmConceptRegister(psMetadataItem *blank, pmConceptParseFunc parse,
    131                         pmConceptFormatFunc format, bool required, pmFPALevel level)
     148                       pmConceptFormatFunc format, pmConceptCopyFunc copy,
     149                       bool required, pmFPALevel level)
    132150{
    133151    PS_ASSERT_PTR_NON_NULL(blank, false);
     
    144162    }
    145163
    146     pmConceptSpec *spec = pmConceptSpecAlloc(blank, parse, format, required); // The concept specification
     164    pmConceptSpec *spec = pmConceptSpecAlloc(blank, parse, format, copy, required); // Concept specification
    147165    psMetadataAdd(target, PS_LIST_TAIL, blank->name, PS_DATA_UNKNOWN | PS_META_REPLACE,
    148166                  "Concepts specification", spec);
     
    188206
    189207
    190 
    191 // Read all registered concepts for the specified level
    192 static bool conceptsRead(psMetadata **specs, // One of the concepts specifications
    193                          pmFPA *fpa,    // The FPA
    194                          pmChip *chip,  // The chip
    195                          pmCell *cell, // The cell
    196                          unsigned int *read,     // What's already been read
    197                          pmConceptSource source, // The source of the concepts to read
    198                          pmConfig *config, // Configuration
    199                          psMetadata *target // Place into which to read the concepts
    200                         )
    201 {
    202     assert(specs);
    203     assert(read);
    204     assert(target);
    205 
    206     if (!conceptsInitialised) {
    207         pmConceptsInit();
    208     }
    209 
    210     // At least one HDU is required for the reading functions
    211     pmHDU *hduLow = pmHDUGetLowest(fpa, chip, cell); // Lowest HDU.
    212     if (!hduLow) {
    213         // Can't do anything --- don't record any success, but don't return an error either
    214         return true;
    215     }
    216     pmHDU *hduHigh = pmHDUGetHighest(fpa, chip, cell); // Highest HDU
    217 
    218     if (cell && (cell->conceptsRead == PM_CONCEPT_SOURCE_NONE)) {
    219         pmConceptsBlankCell(cell);
    220         cell->conceptsRead = PM_CONCEPT_SOURCE_BLANK;
    221     }
    222     if (chip && (chip->conceptsRead == PM_CONCEPT_SOURCE_NONE)) {
    223         pmConceptsBlankChip(chip);
    224         chip->conceptsRead = PM_CONCEPT_SOURCE_BLANK;
    225     }
    226     if (fpa && (fpa->conceptsRead == PM_CONCEPT_SOURCE_NONE)) {
    227         pmConceptsBlankFPA(fpa);
    228         fpa->conceptsRead = PM_CONCEPT_SOURCE_BLANK;
    229     }
    230 
    231     bool success = true;                // Success in reading concepts?
    232     if (source & PM_CONCEPT_SOURCE_CELLS && !(*read & PM_CONCEPT_SOURCE_CELLS) && cell) {
    233         if (p_pmConceptsReadFromCells(target, *specs, cell)) {
    234             *read |= PM_CONCEPT_SOURCE_CELLS;
    235         } else {
    236             psError(PS_ERR_UNKNOWN, false, "Error reading concepts from camera configuration.\n");
    237             success = false;
    238         }
    239     }
    240 
    241     if (source & PM_CONCEPT_SOURCE_DEFAULTS && !(*read & PM_CONCEPT_SOURCE_DEFAULTS)) {
    242         if (p_pmConceptsReadFromDefaults(target, *specs, fpa, chip, cell)) {
    243             *read |= PM_CONCEPT_SOURCE_DEFAULTS;
    244         } else {
    245             psError(PS_ERR_UNKNOWN, false, "Error reading concepts from defaults.\n");
    246             success = false;
    247         }
    248     }
    249 
    250     if (source & PM_CONCEPT_SOURCE_PHU && !(*read & PM_CONCEPT_SOURCE_PHU) && hduHigh->header) {
    251         if (p_pmConceptsReadFromHeader(target, *specs, fpa, chip, cell)) {
    252             *read |= PM_CONCEPT_SOURCE_PHU;
    253         } else {
    254             psError(PS_ERR_UNKNOWN, false, "Error reading concepts from PHU.\n");
    255             success = false;
    256         }
    257     }
    258 
    259     // If there are multiple HDUs, then it may be that one of them hasn't been read yet (hdu->header not set)
    260     if (source & PM_CONCEPT_SOURCE_HEADER && !(*read & PM_CONCEPT_SOURCE_HEADER) &&
    261         hduLow != hduHigh && hduLow->header) {
    262         if (p_pmConceptsReadFromHeader(target, *specs, fpa, chip, cell)) {
    263             *read |= PM_CONCEPT_SOURCE_HEADER;
    264         } else {
    265             psError(PS_ERR_UNKNOWN, false, "Error reading concepts from header.\n");
    266             success = false;
    267         }
    268     }
    269 
    270     #ifdef HAVE_PSDB
    271     if (source & PM_CONCEPT_SOURCE_DATABASE && !(*read & PM_CONCEPT_SOURCE_DATABASE)) {
    272         if (p_pmConceptsReadFromDatabase(target, *specs, fpa, chip, cell, config)) {
    273             *read |= PM_CONCEPT_SOURCE_DATABASE;
    274         } else {
    275             psError(PS_ERR_UNKNOWN, false, "Error reading concepts from database.\n");
    276             success = false;
    277         }
    278     }
    279     #endif
    280 
    281     pmConceptsUpdate(fpa, chip, cell);
    282 
    283     return success;
    284 }
    285 
    286 // Write all registered concepts for the specified level
    287 static bool conceptsWrite(psMetadata **specs, // One of the concepts specifications
    288                           const pmFPA *fpa,   // The FPA
    289                           const pmChip *chip, // The chip
    290                           const pmCell *cell, // The cell
    291                           pmConceptSource source, // The source of the concepts to write
    292                           pmConfig *config, // Configuration
    293                           const psMetadata *concepts // The concepts to write out
    294                          )
    295 {
    296     assert(specs);
    297     assert(concepts);
    298 
    299     if (!conceptsInitialised) {
    300         pmConceptsInit();
    301     }
    302 
    303     psTrace("psModules.concepts", 3, "Writing concepts (%p %p %p): %d\n", fpa, chip, cell, source);
    304 
    305     if (source & PM_CONCEPT_SOURCE_CELLS) {
    306         p_pmConceptsWriteToCells(*specs, cell, concepts);
    307     }
    308     if (source & PM_CONCEPT_SOURCE_DEFAULTS) {
    309         p_pmConceptsWriteToDefaults(*specs, fpa, chip, cell, concepts);
    310     }
    311     if (source & (PM_CONCEPT_SOURCE_PHU | PM_CONCEPT_SOURCE_HEADER)) {
    312         p_pmConceptsWriteToHeader(*specs, fpa, chip, cell, concepts);
    313     }
    314     if (source & PM_CONCEPT_SOURCE_DATABASE) {
    315         p_pmConceptsWriteToDatabase(*specs, fpa, chip, cell, config, concepts);
    316     }
    317 
    318     return true;
    319 }
    320 
    321 
    322 bool pmConceptsRead(pmFPA *fpa, pmChip *chip, pmCell *cell, pmConceptSource source, pmConfig *config)
    323 {
    324     PS_ASSERT_PTR_NON_NULL(fpa, false);
    325     bool success = conceptsRead(&conceptsFPA, fpa, chip, cell, &fpa->conceptsRead, source,
    326                                 config, fpa->concepts);
    327     if (chip) {
    328         success &= conceptsRead(&conceptsChip, fpa, chip, cell, &chip->conceptsRead, source,
    329                                 config, chip->concepts);
    330     }
    331     if (cell) {
    332         success &= conceptsRead(&conceptsCell, fpa, chip, cell, &cell->conceptsRead, source,
    333                                 config, cell->concepts);
    334     }
    335 
    336     return success;
    337 }
    338 
    339 
    340208bool pmConceptsBlankFPA(pmFPA *fpa)
    341209{
     
    345213}
    346214
    347 
    348 bool pmConceptsReadFPA(pmFPA *fpa, pmConceptSource source, bool propagateDown, pmConfig *config)
    349 {
    350     PS_ASSERT_PTR_NON_NULL(fpa, false);
    351     psTrace("psModules.concepts", 5, "Reading FPA concepts: %p %p\n", conceptsFPA, fpa->concepts);
    352     bool success = conceptsRead(&conceptsFPA, fpa, NULL, NULL, &fpa->conceptsRead, source,
    353                                 config, fpa->concepts);
    354     if (propagateDown) {
    355         psArray *chips = fpa->chips;    // Array of chips
    356         for (long i = 0; i < chips->n; i++) {
    357             pmChip *chip = chips->data[i]; // Chip of interest
    358             if (chip) {
    359                 success &= pmConceptsReadChip(chip, source, false, true, config);
    360             }
    361         }
    362     }
    363 
    364     return success;
    365 }
    366 
    367 bool pmConceptsWriteFPA(const pmFPA *fpa, pmConceptSource source, bool propagateDown, pmConfig *config)
    368 {
    369     PS_ASSERT_PTR_NON_NULL(fpa, false);
    370     psTrace("psModules.concepts", 5, "Writing FPA concepts: %p %p\n", conceptsFPA, fpa->concepts);
    371     bool success = conceptsWrite(&conceptsFPA, fpa, NULL, NULL, source, config, fpa->concepts);
    372     if (propagateDown) {
    373         psArray *chips = fpa->chips;        // Array of chips
    374         for (long i = 0; i < chips->n; i++) {
    375             pmChip *chip = chips->data[i];  // Chip of interest
    376             if (chip && !chip->hdu) {
    377                 success &= pmConceptsWriteChip(chip, source, false, true, config);
    378             }
    379         }
    380     }
    381     return success;
    382 }
    383 
    384215bool pmConceptsBlankChip(pmChip *chip)
    385216{
     
    389220}
    390221
    391 bool pmConceptsReadChip(pmChip *chip, pmConceptSource source, bool propagateUp,
    392                         bool propagateDown, pmConfig *config)
    393 {
    394     PS_ASSERT_PTR_NON_NULL(chip, false);
    395     psTrace("psModules.concepts", 5, "Reading chip concepts: %p %p\n", conceptsChip, chip->concepts);
    396     pmFPA *fpa = chip->parent;          // FPA to which the chip belongs
    397     bool success = conceptsRead(&conceptsChip, fpa, chip, NULL, &chip->conceptsRead, source, config,
    398                                 chip->concepts);
    399     if (propagateUp) {
    400         success &= conceptsRead(&conceptsFPA, fpa, chip, NULL, &fpa->conceptsRead, source,
    401                                 config, fpa->concepts);
    402     }
    403     if (propagateDown) {
    404         psArray *cells = chip->cells;        // Array of cells
    405         for (long i = 0; i < cells->n; i++) {
    406             pmCell *cell = cells->data[i];  // Cell of interest
    407             if (cell) {
    408                 success &= pmConceptsReadCell(cell, source, false, config);
    409             }
    410         }
    411     }
    412     return success;
    413 }
    414 
    415 bool pmConceptsWriteChip(const pmChip *chip, pmConceptSource source, bool propagateUp,
    416                          bool propagateDown, pmConfig *config)
    417 {
    418     PS_ASSERT_PTR_NON_NULL(chip, false);
    419     psTrace("psModules.concepts", 5, "Writing chip concepts: %p %p\n", conceptsChip, chip->concepts);
    420     pmFPA *fpa = chip->parent;          // FPA to which the chip belongs
    421     bool success = conceptsWrite(&conceptsChip, fpa, chip, NULL, source, config, chip->concepts);
    422     if (propagateUp && !fpa->hdu) {
    423         success &= conceptsWrite(&conceptsFPA, fpa, chip, NULL, source, config, fpa->concepts);
    424     }
    425     if (propagateDown) {
    426         psArray *cells = chip->cells;        // Array of cells
    427         for (long i = 0; i < cells->n; i++) {
    428             pmCell *cell = cells->data[i];  // Cell of interest
    429             if (cell && !cell->hdu) {
    430                 success &= pmConceptsWriteCell(cell, source, false, config);
    431             }
    432         }
    433     }
    434     return success;
    435 }
    436 
    437222bool pmConceptsBlankCell(pmCell *cell)
    438223{
     
    440225    psTrace("psModules.concepts", 5, "Blanking cell concepts: %p %p\n", conceptsCell, cell->concepts);
    441226    return conceptsBlank(&conceptsCell, cell->concepts);
    442 }
    443 
    444 bool pmConceptsReadCell(pmCell *cell, pmConceptSource source, bool propagateUp, pmConfig *config)
    445 {
    446     PS_ASSERT_PTR_NON_NULL(cell, false);
    447     psTrace("psModules.concepts", 5, "Reading cell concepts: %p %p\n", conceptsCell, cell->concepts);
    448     pmChip *chip = cell->parent;        // Chip to which the cell belongs
    449     pmFPA *fpa = chip->parent;          // FPA to which the chip belongs
    450 
    451     bool success = conceptsRead(&conceptsCell, fpa, chip, cell, &cell->conceptsRead, source, config,
    452                                 cell->concepts);
    453     if (propagateUp) {
    454         success &= conceptsRead(&conceptsChip, fpa, chip, cell, &chip->conceptsRead, source, config,
    455                                 chip->concepts);
    456         success &= conceptsRead(&conceptsFPA, fpa, chip, cell, &fpa->conceptsRead, source, config,
    457                                 fpa->concepts);
    458     }
    459 
    460     return success;
    461 }
    462 
    463 bool pmConceptsWriteCell(const pmCell *cell, pmConceptSource source, bool propagateUp, pmConfig *config)
    464 {
    465     PS_ASSERT_PTR_NON_NULL(cell, false);
    466     psTrace("psModules.concepts", 5, "Writing cell concepts: %p %p\n", conceptsCell, cell->concepts);
    467     pmChip *chip = cell->parent;        // Chip to which the cell belongs
    468     pmFPA *fpa = chip->parent;          // FPA to which the chip belongs
    469 
    470     bool success = conceptsWrite(&conceptsCell, fpa, chip, cell, source, config, cell->concepts);
    471     if (propagateUp) {
    472         if (!chip->hdu) {
    473             success &= conceptsWrite(&conceptsChip, fpa, chip, cell, source, config, chip->concepts);
    474             if (!fpa->hdu) {
    475                 success &= conceptsWrite(&conceptsFPA, fpa, chip, cell, source, config, fpa->concepts);
    476             }
    477         }
    478     }
    479 
    480     return success;
    481227}
    482228
     
    485231static void conceptRegister##SUFFIX(const char *name, /* Name of concept */ \
    486232                                    const char *comment, /* Comment for concept */ \
    487                                     pmConceptParseFunc parse, /* Parsing function */ \
    488                                     pmConceptFormatFunc format, /* Formatting function */ \
     233                                    pmConceptParseFunc parse, /* Parsing function, or NULL */ \
     234                                    pmConceptFormatFunc format, /* Formatting function, or NULL */ \
     235                                    pmConceptCopyFunc copy, /* Copying function, or NULL */ \
    489236                                    bool required, /* Required concept? */ \
    490237                                    pmFPALevel level /* Level at which concept applies */ \
     
    492239{ \
    493240    psMetadataItem *item = psMetadataItemAlloc##TYPENAME(name, comment, DEFAULT); /* Item to add */ \
    494     pmConceptRegister(item, parse, format, required, level); \
     241    pmConceptRegister(item, parse, format, copy, required, level); \
    495242    psFree(item); \
    496243    return; \
     
    515262    psMetadataItem *item = psMetadataItemAlloc(name, PS_DATA_TIME, comment, time);
    516263    psFree(time);
    517     pmConceptRegister(item, p_pmConceptParse_TIME, p_pmConceptFormat_TIME, required, level);
     264    pmConceptRegister(item, p_pmConceptParse_TIME, p_pmConceptFormat_TIME, NULL, required, level);
    518265    psFree(item);
    519266}
     
    521268bool pmConceptsInit(void)
    522269{
     270    if (conceptsInitialised) {
     271        return true;
     272    }
     273
    523274    conceptsInitialised = true;
    524275
     
    527278    bool init = false;                  // Did we initialise anything?
    528279
    529     if (! conceptsFPA) {
     280    if (!conceptsFPA) {
    530281        conceptsFPA = psMetadataAlloc();
    531282        init = true;
    532283
    533284        // Install the standard concepts
    534         conceptRegisterStr("FPA.TELESCOPE", "Telescope of origin", NULL, NULL, false, PM_FPA_LEVEL_FPA);
    535         conceptRegisterStr("FPA.INSTRUMENT", "Instrument name (according to the instrument)", NULL, NULL, false, PM_FPA_LEVEL_FPA);
    536         conceptRegisterStr("FPA.DETECTOR", "Detector name", NULL, NULL, false, PM_FPA_LEVEL_FPA);
    537         conceptRegisterStr("FPA.COMMENT", "Observation comment", NULL, NULL, false, PM_FPA_LEVEL_FPA);
    538         conceptRegisterStr("FPA.OBS.MODE", "Observation mode (eg, survey id)", NULL, NULL, false, PM_FPA_LEVEL_FPA);
    539         conceptRegisterStr("FPA.OBS.GROUP", "Observation group (eg, associated images)", NULL, NULL, false, PM_FPA_LEVEL_FPA);
    540         conceptRegisterF32("FPA.FOCUS", "Telescope focus", NULL, NULL, false, PM_FPA_LEVEL_FPA);
    541         conceptRegisterF32("FPA.AIRMASS", "Airmass at boresight", NULL, NULL, false, PM_FPA_LEVEL_FPA);
    542         conceptRegisterStr("FPA.FILTERID", "Filter used (parsed, abstract name)", p_pmConceptParse_FPA_FILTER, p_pmConceptFormat_FPA_FILTER, false, PM_FPA_LEVEL_FPA);
    543         conceptRegisterStr("FPA.FILTER", "Filter used (instrument name)", false, NULL, NULL, PM_FPA_LEVEL_FPA);
    544         conceptRegisterF32("FPA.POSANGLE", "Position angle of instrument", NULL, NULL, false, PM_FPA_LEVEL_FPA);
    545         conceptRegisterF32("FPA.ROTANGLE", "Rotator angle of instrument", NULL, NULL, false, PM_FPA_LEVEL_FPA);
    546         conceptRegisterStr("FPA.RADECSYS", "Celestial coordinate system", NULL, NULL, false, PM_FPA_LEVEL_FPA);
    547         conceptRegisterF64("FPA.RA", "Right Ascension of boresight", p_pmConceptParse_FPA_Coords, p_pmConceptFormat_FPA_Coords, false, PM_FPA_LEVEL_FPA);
    548         conceptRegisterF64("FPA.DEC", "Declination of boresight", p_pmConceptParse_FPA_Coords, p_pmConceptFormat_FPA_Coords, false, PM_FPA_LEVEL_FPA);
    549         conceptRegisterF64("FPA.LONGITUDE", "West longitude of observatory", p_pmConceptParse_FPA_Coords, p_pmConceptFormat_FPA_Coords, false, PM_FPA_LEVEL_FPA);
    550         conceptRegisterF64("FPA.LATITUDE", "Latitude of observatory", p_pmConceptParse_FPA_Coords, p_pmConceptFormat_FPA_Coords, false, PM_FPA_LEVEL_FPA);
    551         conceptRegisterF32("FPA.ELEVATION", "Elevation of observatory (metres)", NULL, NULL, false, PM_FPA_LEVEL_FPA);
    552         conceptRegisterStr("FPA.OBSTYPE", "Type of observation", NULL, NULL, false, PM_FPA_LEVEL_FPA);
    553         conceptRegisterStr("FPA.OBJECT", "Object of observation", NULL, NULL, false, PM_FPA_LEVEL_FPA);
    554         conceptRegisterF64("FPA.ALT", "Altitude of boresight", NULL, NULL, false, PM_FPA_LEVEL_FPA);
    555         conceptRegisterF64("FPA.AZ", "Azimuth of boresight", NULL, NULL, false, PM_FPA_LEVEL_FPA);
    556         conceptRegisterEnum("FPA.TIMESYS", "Time system", p_pmConceptParse_TIMESYS, p_pmConceptFormat_TIMESYS, false, PM_FPA_LEVEL_FPA);
     285        conceptRegisterStr("FPA.TELESCOPE", "Telescope of origin", NULL, NULL, NULL, false, PM_FPA_LEVEL_FPA);
     286        conceptRegisterStr("FPA.INSTRUMENT", "Instrument name (according to the instrument)", NULL, NULL, NULL, false, PM_FPA_LEVEL_FPA);
     287        conceptRegisterStr("FPA.DETECTOR", "Detector name", NULL, NULL, NULL, false, PM_FPA_LEVEL_FPA);
     288        conceptRegisterStr("FPA.COMMENT", "Observation comment", NULL, NULL, NULL, false, PM_FPA_LEVEL_FPA);
     289        conceptRegisterStr("FPA.OBS.MODE", "Observation mode (eg, survey id)", NULL, NULL, NULL, false, PM_FPA_LEVEL_FPA);
     290        conceptRegisterStr("FPA.OBS.GROUP", "Observation group (eg, associated images)", NULL, NULL, NULL, false, PM_FPA_LEVEL_FPA);
     291        conceptRegisterF32("FPA.FOCUS", "Telescope focus", NULL, NULL, NULL, false, PM_FPA_LEVEL_FPA);
     292        conceptRegisterF32("FPA.AIRMASS", "Airmass at boresight", NULL, NULL, NULL, false, PM_FPA_LEVEL_FPA);
     293        conceptRegisterStr("FPA.FILTERID", "Filter used (parsed, abstract name)", p_pmConceptParse_FPA_FILTER, p_pmConceptFormat_FPA_FILTER, NULL, false, PM_FPA_LEVEL_FPA);
     294        conceptRegisterStr("FPA.FILTER", "Filter used (instrument name)", NULL, NULL, NULL, false, PM_FPA_LEVEL_FPA);
     295        conceptRegisterF32("FPA.POSANGLE", "Position angle of instrument", NULL, NULL, NULL, false, PM_FPA_LEVEL_FPA);
     296        conceptRegisterF32("FPA.ROTANGLE", "Rotator angle of instrument", NULL, NULL, NULL, false, PM_FPA_LEVEL_FPA);
     297        conceptRegisterStr("FPA.RADECSYS", "Celestial coordinate system", NULL, NULL, NULL, false, PM_FPA_LEVEL_FPA);
     298        conceptRegisterF64("FPA.RA", "Right Ascension of boresight", p_pmConceptParse_FPA_Coords, p_pmConceptFormat_FPA_Coords, NULL, false, PM_FPA_LEVEL_FPA);
     299        conceptRegisterF64("FPA.DEC", "Declination of boresight", p_pmConceptParse_FPA_Coords, p_pmConceptFormat_FPA_Coords, NULL, false, PM_FPA_LEVEL_FPA);
     300        conceptRegisterF64("FPA.LONGITUDE", "West longitude of observatory", p_pmConceptParse_FPA_Coords, p_pmConceptFormat_FPA_Coords, NULL, false, PM_FPA_LEVEL_FPA);
     301        conceptRegisterF64("FPA.LATITUDE", "Latitude of observatory", p_pmConceptParse_FPA_Coords, p_pmConceptFormat_FPA_Coords, NULL, false, PM_FPA_LEVEL_FPA);
     302        conceptRegisterF32("FPA.ELEVATION", "Elevation of observatory (metres)", NULL, NULL, NULL, false, PM_FPA_LEVEL_FPA);
     303        conceptRegisterStr("FPA.OBSTYPE", "Type of observation", NULL, NULL, NULL, false, PM_FPA_LEVEL_FPA);
     304        conceptRegisterStr("FPA.OBJECT", "Object of observation", NULL, NULL, NULL, false, PM_FPA_LEVEL_FPA);
     305        conceptRegisterF64("FPA.ALT", "Altitude of boresight", NULL, NULL, NULL, false, PM_FPA_LEVEL_FPA);
     306        conceptRegisterF64("FPA.AZ", "Azimuth of boresight", NULL, NULL, NULL, false, PM_FPA_LEVEL_FPA);
     307        conceptRegisterEnum("FPA.TIMESYS", "Time system", p_pmConceptParse_TIMESYS, p_pmConceptFormat_TIMESYS, p_pmConceptCopy_TIMESYS, false, PM_FPA_LEVEL_FPA);
    557308        conceptRegisterTime("FPA.TIME", "Time of exposure", false, PM_FPA_LEVEL_FPA);
    558         conceptRegisterF32("FPA.TEMP", "Temperature of focal plane", NULL, NULL, false, PM_FPA_LEVEL_FPA);
    559         conceptRegisterF32("FPA.M1X", "Primary Mirror X Position", NULL, NULL, false, PM_FPA_LEVEL_FPA);
    560         conceptRegisterF32("FPA.M1Y", "Primary Mirror Y Position", NULL, NULL, false, PM_FPA_LEVEL_FPA);
    561         conceptRegisterF32("FPA.M1Z", "Primary Mirror Z Position", NULL, NULL, false, PM_FPA_LEVEL_FPA);
    562         conceptRegisterF32("FPA.M1TIP", "Primary Mirror TIP", NULL, NULL, false, PM_FPA_LEVEL_FPA);
    563         conceptRegisterF32("FPA.M1TILT", "Primary Mirror TILT", NULL, NULL, false, PM_FPA_LEVEL_FPA);
    564         conceptRegisterF32("FPA.M2X", "Primary Mirror X Position", NULL, NULL, false, PM_FPA_LEVEL_FPA);
    565         conceptRegisterF32("FPA.M2Y", "Primary Mirror Y Position", NULL, NULL, false, PM_FPA_LEVEL_FPA);
    566         conceptRegisterF32("FPA.M2Z", "Primary Mirror Z Position", NULL, NULL, false, PM_FPA_LEVEL_FPA);
    567         conceptRegisterF32("FPA.M2TIP", "Primary Mirror TIP", NULL, NULL, false, PM_FPA_LEVEL_FPA);
    568         conceptRegisterF32("FPA.M2TILT", "Primary Mirror TILT", NULL, NULL, false, PM_FPA_LEVEL_FPA);
    569         conceptRegisterF32("FPA.ENV.TEMP", "Environment: Temperature", NULL, NULL, false, PM_FPA_LEVEL_FPA);
    570         conceptRegisterF32("FPA.ENV.HUMID", "Environment: Humidity", NULL, NULL, false, PM_FPA_LEVEL_FPA);
    571         conceptRegisterF32("FPA.ENV.WIND", "Environment: Wind speed", NULL, NULL, false, PM_FPA_LEVEL_FPA);
    572         conceptRegisterF32("FPA.ENV.DIR", "Environment: Wind direction", NULL, NULL, false, PM_FPA_LEVEL_FPA);
    573         conceptRegisterF32("FPA.TELTEMP.M1", "Telescope Temperatures: M1", p_pmConceptParse_TELTEMPS, NULL, false, PM_FPA_LEVEL_FPA);
    574         conceptRegisterF32("FPA.TELTEMP.M1CELL", "Telescope Temperatures: M1 cell", p_pmConceptParse_TELTEMPS, NULL, false, PM_FPA_LEVEL_FPA);
    575         conceptRegisterF32("FPA.TELTEMP.M2", "Telescope Temperatures: M2", p_pmConceptParse_TELTEMPS, NULL, false, PM_FPA_LEVEL_FPA);
    576         conceptRegisterF32("FPA.TELTEMP.SPIDER", "Telescope Temperatures: spider", p_pmConceptParse_TELTEMPS, NULL, false, PM_FPA_LEVEL_FPA);
    577         conceptRegisterF32("FPA.TELTEMP.TRUSS", "Telescope Temperatures: truss", p_pmConceptParse_TELTEMPS, NULL, false, PM_FPA_LEVEL_FPA);
    578         conceptRegisterF32("FPA.TELTEMP.EXTRA", "Telescope Temperatures: extra", p_pmConceptParse_TELTEMPS, NULL, false, PM_FPA_LEVEL_FPA);
    579         conceptRegisterF32("FPA.PON.TIME", "Power On Time", NULL, NULL, false, PM_FPA_LEVEL_FPA);
    580         conceptRegisterF32("FPA.EXPOSURE", "Exposure time (sec)", NULL, NULL, false, PM_FPA_LEVEL_FPA);
    581     }
    582     if (! conceptsChip) {
     309        conceptRegisterF32("FPA.TEMP", "Temperature of focal plane", NULL, NULL, NULL, false, PM_FPA_LEVEL_FPA);
     310        conceptRegisterF32("FPA.M1X", "Primary Mirror X Position", NULL, NULL, NULL, false, PM_FPA_LEVEL_FPA);
     311        conceptRegisterF32("FPA.M1Y", "Primary Mirror Y Position", NULL, NULL, NULL, false, PM_FPA_LEVEL_FPA);
     312        conceptRegisterF32("FPA.M1Z", "Primary Mirror Z Position", NULL, NULL, NULL, false, PM_FPA_LEVEL_FPA);
     313        conceptRegisterF32("FPA.M1TIP", "Primary Mirror TIP", NULL, NULL, NULL, false, PM_FPA_LEVEL_FPA);
     314        conceptRegisterF32("FPA.M1TILT", "Primary Mirror TILT", NULL, NULL, NULL, false, PM_FPA_LEVEL_FPA);
     315        conceptRegisterF32("FPA.M2X", "Primary Mirror X Position", NULL, NULL, NULL, false, PM_FPA_LEVEL_FPA);
     316        conceptRegisterF32("FPA.M2Y", "Primary Mirror Y Position", NULL, NULL, NULL, false, PM_FPA_LEVEL_FPA);
     317        conceptRegisterF32("FPA.M2Z", "Primary Mirror Z Position", NULL, NULL, NULL, false, PM_FPA_LEVEL_FPA);
     318        conceptRegisterF32("FPA.M2TIP", "Primary Mirror TIP", NULL, NULL, NULL, false, PM_FPA_LEVEL_FPA);
     319        conceptRegisterF32("FPA.M2TILT", "Primary Mirror TILT", NULL, NULL, NULL, false, PM_FPA_LEVEL_FPA);
     320        conceptRegisterF32("FPA.ENV.TEMP", "Environment: Temperature", NULL, NULL, NULL, false, PM_FPA_LEVEL_FPA);
     321        conceptRegisterF32("FPA.ENV.HUMID", "Environment: Humidity", NULL, NULL, NULL, false, PM_FPA_LEVEL_FPA);
     322        conceptRegisterF32("FPA.ENV.WIND", "Environment: Wind speed", NULL, NULL, NULL, false, PM_FPA_LEVEL_FPA);
     323        conceptRegisterF32("FPA.ENV.DIR", "Environment: Wind direction", NULL, NULL, NULL, false, PM_FPA_LEVEL_FPA);
     324        conceptRegisterF32("FPA.TELTEMP.M1", "Telescope Temperatures: M1", p_pmConceptParse_TELTEMPS, NULL, NULL, false, PM_FPA_LEVEL_FPA);
     325        conceptRegisterF32("FPA.TELTEMP.M1CELL", "Telescope Temperatures: M1 cell", p_pmConceptParse_TELTEMPS, NULL, NULL, false, PM_FPA_LEVEL_FPA);
     326        conceptRegisterF32("FPA.TELTEMP.M2", "Telescope Temperatures: M2", p_pmConceptParse_TELTEMPS, NULL, NULL, false, PM_FPA_LEVEL_FPA);
     327        conceptRegisterF32("FPA.TELTEMP.SPIDER", "Telescope Temperatures: spider", p_pmConceptParse_TELTEMPS, NULL, NULL, false, PM_FPA_LEVEL_FPA);
     328        conceptRegisterF32("FPA.TELTEMP.TRUSS", "Telescope Temperatures: truss", p_pmConceptParse_TELTEMPS, NULL, NULL, false, PM_FPA_LEVEL_FPA);
     329        conceptRegisterF32("FPA.TELTEMP.EXTRA", "Telescope Temperatures: extra", p_pmConceptParse_TELTEMPS, NULL, NULL, false, PM_FPA_LEVEL_FPA);
     330        conceptRegisterF32("FPA.PON.TIME", "Power On Time", NULL, NULL, NULL, false, PM_FPA_LEVEL_FPA);
     331        conceptRegisterF32("FPA.EXPOSURE", "Exposure time (sec)", NULL, NULL, NULL, false, PM_FPA_LEVEL_FPA);
     332    }
     333    if (!conceptsChip) {
    583334        conceptsChip = psMetadataAlloc();
    584335        init = true;
    585336
    586337        // Install the standard concepts
    587         conceptRegisterS32("CHIP.XPARITY", "Orientation in x compared to the rest of the FPA", NULL, NULL, true, PM_FPA_LEVEL_CHIP);
    588         conceptRegisterS32("CHIP.YPARITY", "Orientation in y compared to the rest of the FPA", NULL, NULL, true, PM_FPA_LEVEL_CHIP);
    589         conceptRegisterS32("CHIP.X0", "Position of (0,0) on the FPA",p_pmConceptParse_Positions,p_pmConceptFormat_Positions, true, PM_FPA_LEVEL_CHIP);
    590         conceptRegisterS32("CHIP.Y0", "Position of (0,0) on the FPA",p_pmConceptParse_Positions,p_pmConceptFormat_Positions, true, PM_FPA_LEVEL_CHIP);
    591         conceptRegisterS32("CHIP.XSIZE", "Size of chip (pixels)", NULL, NULL, true, PM_FPA_LEVEL_CHIP);
    592         conceptRegisterS32("CHIP.YSIZE", "Size of chip (pixels)", NULL, NULL, true, PM_FPA_LEVEL_CHIP);
    593         conceptRegisterF32("CHIP.TEMP", "Temperature of chip", NULL, NULL, false, PM_FPA_LEVEL_CHIP);
    594         conceptRegisterStr("CHIP.ID", "Chip identifier", NULL, NULL, false, PM_FPA_LEVEL_CHIP);
    595     }
    596 
    597     if (! conceptsCell) {
     338        conceptRegisterS32("CHIP.XPARITY", "Orientation in x compared to the rest of the FPA", NULL, NULL, NULL, true, PM_FPA_LEVEL_CHIP);
     339        conceptRegisterS32("CHIP.YPARITY", "Orientation in y compared to the rest of the FPA", NULL, NULL, NULL, true, PM_FPA_LEVEL_CHIP);
     340        conceptRegisterS32("CHIP.X0", "Position of (0,0) on the FPA",p_pmConceptParse_Positions,p_pmConceptFormat_Positions, NULL, true, PM_FPA_LEVEL_CHIP);
     341        conceptRegisterS32("CHIP.Y0", "Position of (0,0) on the FPA",p_pmConceptParse_Positions,p_pmConceptFormat_Positions, NULL, true, PM_FPA_LEVEL_CHIP);
     342        conceptRegisterS32("CHIP.XSIZE", "Size of chip (pixels)", NULL, NULL, NULL, true, PM_FPA_LEVEL_CHIP);
     343        conceptRegisterS32("CHIP.YSIZE", "Size of chip (pixels)", NULL, NULL, NULL, true, PM_FPA_LEVEL_CHIP);
     344        conceptRegisterF32("CHIP.TEMP", "Temperature of chip", NULL, NULL, NULL, false, PM_FPA_LEVEL_CHIP);
     345        conceptRegisterStr("CHIP.ID", "Chip identifier", NULL, NULL, NULL, false, PM_FPA_LEVEL_CHIP);
     346    }
     347
     348    if (!conceptsCell) {
    598349        conceptsCell = psMetadataAlloc();
    599350        init = true;
    600351
    601352        // Install the standard concepts
    602         conceptRegisterF32("CELL.GAIN", "CCD gain (e/count)", NULL, NULL, true, PM_FPA_LEVEL_CELL);
    603         conceptRegisterF32("CELL.READNOISE", "CCD read noise (e)", p_pmConceptParse_CELL_READNOISE, p_pmConceptFormat_CELL_READNOISE, true, PM_FPA_LEVEL_CELL);
    604         conceptRegisterF32("CELL.SATURATION", "Saturation level (counts)", NULL, NULL, true, PM_FPA_LEVEL_CELL);
    605         conceptRegisterF32("CELL.BAD", "Bad level (counts)", NULL, NULL, true, PM_FPA_LEVEL_CELL);
    606         conceptRegisterS32("CELL.XPARITY", "Orientation in x compared to the rest of the chip", NULL, NULL, true, PM_FPA_LEVEL_CELL);
    607         conceptRegisterS32("CELL.YPARITY", "Orientation in y compared to the rest of the chip", NULL, NULL, true, PM_FPA_LEVEL_CELL);
    608         conceptRegisterS32("CELL.READDIR", "Read direction, rows=1, cols=2", NULL, NULL, true, PM_FPA_LEVEL_CELL);
     353        conceptRegisterF32("CELL.GAIN", "CCD gain (e/count)", NULL, NULL, NULL, true, PM_FPA_LEVEL_CELL);
     354        conceptRegisterF32("CELL.READNOISE", "CCD read noise (e)", p_pmConceptParse_CELL_READNOISE, p_pmConceptFormat_CELL_READNOISE, NULL, true, PM_FPA_LEVEL_CELL);
     355        conceptRegisterF32("CELL.SATURATION", "Saturation level (counts)", NULL, NULL, NULL, true, PM_FPA_LEVEL_CELL);
     356        conceptRegisterF32("CELL.BAD", "Bad level (counts)", NULL, NULL, NULL, true, PM_FPA_LEVEL_CELL);
     357        conceptRegisterS32("CELL.XPARITY", "Orientation in x compared to the rest of the chip", NULL, NULL, NULL, true, PM_FPA_LEVEL_CELL);
     358        conceptRegisterS32("CELL.YPARITY", "Orientation in y compared to the rest of the chip", NULL, NULL, NULL, true, PM_FPA_LEVEL_CELL);
     359        conceptRegisterS32("CELL.READDIR", "Read direction, rows=1, cols=2", NULL, NULL, NULL, true, PM_FPA_LEVEL_CELL);
    609360
    610361        // These (CELL.EXPOSURE and CELL.DARKTIME) used to be READOUT.EXPOSURE and READOUT.DARKTIME, but that
     
    613364        // readout is a plane in a 3D image.  We'll have to dream up some additional suffix to specify these,
    614365        // but for now....
    615         conceptRegisterF32("CELL.EXPOSURE", "Exposure time (sec)", NULL, NULL, false, PM_FPA_LEVEL_CELL);
    616         conceptRegisterF32("CELL.DARKTIME", "Time since flush (sec)", NULL, NULL, false, PM_FPA_LEVEL_CELL);
    617 
    618         conceptRegisterS32("CELL.XBIN", "Binning in x", p_pmConceptParse_CELL_Binning,p_pmConceptFormat_CELL_XBIN, true, PM_FPA_LEVEL_CELL);
    619         conceptRegisterS32("CELL.YBIN", "Binning in y",p_pmConceptParse_CELL_Binning,p_pmConceptFormat_CELL_YBIN, true, PM_FPA_LEVEL_CELL);
    620         conceptRegisterEnum("CELL.TIMESYS", "Time system", p_pmConceptParse_TIMESYS,p_pmConceptFormat_TIMESYS, false, PM_FPA_LEVEL_CELL);
     366        conceptRegisterF32("CELL.EXPOSURE", "Exposure time (sec)", NULL, NULL, NULL, false, PM_FPA_LEVEL_CELL);
     367        conceptRegisterF32("CELL.DARKTIME", "Time since flush (sec)", NULL, NULL, NULL, false, PM_FPA_LEVEL_CELL);
     368
     369        conceptRegisterS32("CELL.XBIN", "Binning in x", p_pmConceptParse_CELL_Binning,p_pmConceptFormat_CELL_XBIN, NULL, true, PM_FPA_LEVEL_CELL);
     370        conceptRegisterS32("CELL.YBIN", "Binning in y",p_pmConceptParse_CELL_Binning,p_pmConceptFormat_CELL_YBIN, NULL, true, PM_FPA_LEVEL_CELL);
     371        conceptRegisterEnum("CELL.TIMESYS", "Time system", p_pmConceptParse_TIMESYS,p_pmConceptFormat_TIMESYS, p_pmConceptCopy_TIMESYS, false, PM_FPA_LEVEL_CELL);
    621372        conceptRegisterTime("CELL.TIME", "Time of exposure", false, PM_FPA_LEVEL_CELL);
    622         conceptRegisterS32("CELL.X0", "Position of (0,0) on the chip",p_pmConceptParse_Positions,p_pmConceptFormat_Positions, true, PM_FPA_LEVEL_CELL);
    623         conceptRegisterS32("CELL.Y0", "Position of (0,0) on the chip",p_pmConceptParse_Positions,p_pmConceptFormat_Positions, true, PM_FPA_LEVEL_CELL);
    624         conceptRegisterS32("CELL.XSIZE", "Size of cell (pixels)", NULL, NULL, true, PM_FPA_LEVEL_CELL);
    625         conceptRegisterS32("CELL.YSIZE", "Size of cell (pixels)", NULL, NULL, true, PM_FPA_LEVEL_CELL);
    626         conceptRegisterS32("CELL.XWINDOW", "Start of cell window (pixels)",p_pmConceptParse_Positions,p_pmConceptFormat_Positions, true, PM_FPA_LEVEL_CELL);
    627         conceptRegisterS32("CELL.YWINDOW", "Start of cell window (pixels)",p_pmConceptParse_Positions,p_pmConceptFormat_Positions, true, PM_FPA_LEVEL_CELL);
    628         conceptRegisterF32("CELL.VARFACTOR", "Variance factor for conversion from large to small scales", NULL, NULL, true, PM_FPA_LEVEL_CELL);
     373        conceptRegisterS32("CELL.X0", "Position of (0,0) on the chip",p_pmConceptParse_Positions,p_pmConceptFormat_Positions, NULL, true, PM_FPA_LEVEL_CELL);
     374        conceptRegisterS32("CELL.Y0", "Position of (0,0) on the chip",p_pmConceptParse_Positions,p_pmConceptFormat_Positions, NULL, true, PM_FPA_LEVEL_CELL);
     375        conceptRegisterS32("CELL.XSIZE", "Size of cell (pixels)", NULL, NULL, NULL, true, PM_FPA_LEVEL_CELL);
     376        conceptRegisterS32("CELL.YSIZE", "Size of cell (pixels)", NULL, NULL, NULL, true, PM_FPA_LEVEL_CELL);
     377        conceptRegisterS32("CELL.XWINDOW", "Start of cell window (pixels)",p_pmConceptParse_Positions,p_pmConceptFormat_Positions, NULL, true, PM_FPA_LEVEL_CELL);
     378        conceptRegisterS32("CELL.YWINDOW", "Start of cell window (pixels)",p_pmConceptParse_Positions,p_pmConceptFormat_Positions, NULL, true, PM_FPA_LEVEL_CELL);
     379        conceptRegisterF32("CELL.VARFACTOR", "Variance factor for conversion from large to small scales", NULL, NULL, NULL, true, PM_FPA_LEVEL_CELL);
    629380
    630381        // CELL.TRIMSEC
     
    635386                                          "Trim section", trimsec);
    636387            psFree(trimsec);
    637             pmConceptRegister(cellTrimsec, p_pmConceptParse_CELL_TRIMSEC,p_pmConceptFormat_CELL_TRIMSEC, true, PM_FPA_LEVEL_CELL);
     388            pmConceptRegister(cellTrimsec, p_pmConceptParse_CELL_TRIMSEC,p_pmConceptFormat_CELL_TRIMSEC, NULL, true, PM_FPA_LEVEL_CELL);
    638389            psFree(cellTrimsec);
    639390        }
     
    645396                                          "Bias sections", biassecs);
    646397            psFree(biassecs);
    647             pmConceptRegister(cellBiassec, p_pmConceptParse_CELL_BIASSEC, p_pmConceptFormat_CELL_BIASSEC, true, PM_FPA_LEVEL_CELL);
     398            pmConceptRegister(cellBiassec, p_pmConceptParse_CELL_BIASSEC, p_pmConceptFormat_CELL_BIASSEC, NULL, true, PM_FPA_LEVEL_CELL);
    648399            psFree(cellBiassec);
    649400        }
     
    666417
    667418    conceptsInitialised = false;
    668 }
    669 
    670 
    671 // List of concepts not to copy, for each level.
    672 // Must be NULL-terminated
    673 static const char *dontCopyConceptsFPA[] = { "FPA.OBS", "FPA.NAME", "FPA.CAMERA", 0 };
    674 static const char *dontCopyConceptsChip[] = { "CHIP.NAME", 0 };
    675 static const char *dontCopyConceptsCell[] = { "CELL.NAME", "CELL.TRIMSEC", "CELL.BIASSEC", 0 };
    676 
    677 // Copy concepts from a source container to a target container, avoiding certain entries
    678 static void copyConcepts(psMetadata *target, // Target metadata container
    679                          psMetadata *source, // Source metadata container
    680                          const char *dontCopyConcepts[] // Don't copy these concepts
    681                          )
    682 {
    683     assert(target);
    684     assert(source);
    685     assert(dontCopyConcepts);
    686 
    687     psMetadataIterator *iter = psMetadataIteratorAlloc(source, PS_LIST_HEAD, NULL);
    688     psMetadataItem *item;               // Item from iteration
    689     while ((item = psMetadataGetAndIncrement(iter))) {
    690         const char *name = item->name;  // Name of concept
    691         bool copyOK = true;            // OK to copy
    692         for (int i = 0; dontCopyConcepts[i] && copyOK; i++) {
    693             if (!strcmp(name, dontCopyConcepts[i])) {
    694                 copyOK = false;
    695             }
    696         }
    697         if (!copyOK) {
    698             continue;
    699         }
    700         psMetadataItem *new = psMetadataItemCopy(item); // New item, a copy of the old
    701         psMetadataAddItem(target, new, PS_LIST_TAIL, PS_META_REPLACE);
    702         psFree(new);                    // Drop reference
    703     }
    704     psFree(iter);
    705 
    706     return;
    707 }
    708 
    709 
    710 bool pmFPACopyConcepts(pmFPA *target, const pmFPA *source)
    711 {
    712     PS_ASSERT_PTR_NON_NULL(target, false);
    713     PS_ASSERT_PTR_NON_NULL(source, false);
    714 
    715     // Copy FPA concepts
    716     copyConcepts(target->concepts, source->concepts, dontCopyConceptsFPA);
    717 
    718     // Copy chip concepts
    719     psArray *targetChips = target->chips; // Chips in target
    720     psArray *sourceChips = source->chips; // Chips in source
    721     if (targetChips->n != sourceChips->n) {
    722         psError(PS_ERR_IO, true, "Number of chips in target (%ld) and source (%ld) differ --- unable to copy "
    723                 "concepts.\n", targetChips->n, sourceChips->n);
    724         return false;
    725     }
    726     for (int i = 0; i < targetChips->n; i++) {
    727         pmChip *targetChip = targetChips->data[i]; // Target chip of interest
    728         pmChip *sourceChip = sourceChips->data[i]; // Source chip of interest
    729         if (! targetChip || ! sourceChip) {
    730             continue;
    731         }
    732 
    733         copyConcepts(targetChip->concepts, sourceChip->concepts, dontCopyConceptsChip);
    734 
    735         // Copy cell concepts
    736         psArray *targetCells = targetChip->cells; // Cells in target
    737         psArray *sourceCells = sourceChip->cells; // Cells in source
    738         if (targetCells->n != sourceCells->n) {
    739             psError(PS_ERR_IO, true, "Number of cells in target (%ld) and source (%ld) differ for chip %d ---"
    740                     " unable to copy concepts.\n", targetCells->n, sourceCells->n, i);
    741             return false;
    742         }
    743         for (int j = 0; j < targetCells->n; j++) {
    744             pmCell *targetCell = targetCells->data[j]; // Target chip of interest
    745             pmCell *sourceCell = sourceCells->data[j]; // Source chip of interest
    746             if (! targetCell || ! sourceCell) {
    747                 continue;
    748             }
    749 
    750             copyConcepts(targetCell->concepts, sourceCell->concepts, dontCopyConceptsCell);
    751         }
    752     }
    753 
    754     return true;
    755 }
    756 
    757 
    758 bool pmConceptsCopyFPA(pmFPA *target, const pmFPA *source, bool chips, bool cells)
    759 {
    760     PS_ASSERT_PTR_NON_NULL(target, false);
    761     PS_ASSERT_PTR_NON_NULL(source, false);
    762 
    763     // Copy FPA concepts
    764     copyConcepts(target->concepts, source->concepts, dontCopyConceptsFPA);
    765 
    766     // Copy chip concepts
    767     bool status = true;                 // Status of chips
    768     if (chips) {
    769         psArray *targetChips = target->chips; // Chips in target
    770         psArray *sourceChips = source->chips; // Chips in source
    771         if (targetChips->n != sourceChips->n) {
    772             psError(PS_ERR_IO, true,
    773                     "Number of chips in target (%ld) and source (%ld) differ --- unable to copy concepts.",
    774                     targetChips->n, sourceChips->n);
    775             return false;
    776         }
    777         for (int i = 0; i < targetChips->n; i++) {
    778             pmChip *targetChip = targetChips->data[i]; // Target chip of interest
    779             pmChip *sourceChip = sourceChips->data[i]; // Source chip of interest
    780             if (! targetChip || ! sourceChip) {
    781                 continue;
    782             }
    783 
    784             status &= pmConceptsCopyChip(targetChip, sourceChip, cells);
    785         }
    786     }
    787 
    788     return status;
    789 }
    790 
    791 bool pmConceptsCopyChip(pmChip *target, const pmChip *source, bool cells)
    792 {
    793     PS_ASSERT_PTR_NON_NULL(target, false);
    794     PS_ASSERT_PTR_NON_NULL(source, false);
    795 
    796     // Copy chip concepts
    797     copyConcepts(target->concepts, source->concepts, dontCopyConceptsChip);
    798 
    799     // Copy cell concepts
    800     bool status = true;                 // Status of cells
    801     if (cells) {
    802         psArray *targetCells = target->cells; // Cells in target
    803         psArray *sourceCells = source->cells; // Cells in source
    804         if (targetCells->n != sourceCells->n) {
    805             psError(PS_ERR_IO, true,
    806                     "Number of cells in target (%ld) and source (%ld) differ --- unable to copy concepts.",
    807                     targetCells->n, sourceCells->n);
    808             return false;
    809         }
    810         for (int j = 0; j < targetCells->n; j++) {
    811             pmCell *targetCell = targetCells->data[j]; // Target chip of interest
    812             pmCell *sourceCell = sourceCells->data[j]; // Source chip of interest
    813             if (! targetCell || ! sourceCell) {
    814                 continue;
    815             }
    816 
    817             status &= pmConceptsCopyCell(targetCell, sourceCell);
    818         }
    819     }
    820 
    821     return status;
    822 }
    823 
    824 
    825 bool pmConceptsCopyCell(pmCell *target, const pmCell *source)
    826 {
    827     PS_ASSERT_PTR_NON_NULL(target, false);
    828     PS_ASSERT_PTR_NON_NULL(source, false);
    829 
    830     copyConcepts(target->concepts, source->concepts, dontCopyConceptsCell);
    831 
    832     return true;
    833419}
    834420
Note: See TracChangeset for help on using the changeset viewer.