New handling of shared memory in new ACAPS

1 Previous situation

Two shared memory segments were created and accessed:

  1. one segment described by struct AstroData
  2. one segment described by struct ACAPSstatus, this structure in turn had a struct DualPortedRAM as its last member.

The combined size of these segments is of order 3000 bytes.

On Linux a shared memory segment has a typical minimum size of 1 page (4096 bytes), so it makes sense to create only one shared memory segment to hold both structures.

The dual ported RAM was a concept needed on OS-9 systems, there is really no need to treat this part differently. But in order to facilitate porting our legacy ACAPS code, I have kept that type of structure around.

At least the following programs were using the shared memory segments, and usually also local copies of them:

  • watchdog
  • loop
  • astra
  • sub
  • cid
  • display

Typically, the shared memory holding the struct ACAPSstatus was accessed via a struct ACAPSstatus *SysStat, and the local copy via struct ACAPSstatus acs (note: not a pointer!)

2 Proposed new solution

The files

  • acaps.h
  • acapsdat.c
  • astrodat.h
  • astrodat.c

are replaced by

  • memory.h
  • memory.c

The memory layout in the shared segment is as follows:

struct MemoryLayout {
    struct AstroData oso;
    struct AcapsStatus acs;
    struct DualPortedRAM dp;   /* no longer inside of AcapsStatus */
};

Note the new spelling of AcapsStatus.

Linking your code with memory.c, you get access to the following methods:

  • void createShared() to link to the shared memory segment, create if necessary.
  • void linkShared() to link to the shared memory segment.
  • void unlinkShared() to unlink from the shared memory segment.
  • void killShared() to destroy the shared memory segment.
  • void lockShared() to block other programs from access via a semaphore.
  • void unlockShared() to allow access to other programs.

We should probably have a system service, which creates the shared memory during system boot.

To get pointers to the three structures in shared memory, use the following methods. They will all return a NULL pointer if one has not yet linked to the shared memory:

  • AstroData *sharedAstroData()
  • AcapsStatus *sharedAcapsStatus()
  • DualPortedRAM *sharedDualPortedRAM()

To get pointers to local copies, use the following methods, which are available even without linking to the shared segment:

  • AstroData *localAstroData()
  • AcapsStatus *localAcapsStatus()
  • DualPortedRAM *localDualPortedRAM()

Instead of old code like this

/* for local access */
struct ACAPSstatus acs;    /* or extern struct ACAPSstatus acs; */
...
acs.dp.EncUsed = ENCBOTH;  /* just as an example */

/* for shared access */
struct ACAPSstatus *SysStat;
SysStat = LinkAcaps();
...
SysStat->dp.EncUsed = ENCBOTH;  /* just as an example */

one now needs to use

/* for local access */
struct AcapsStatus *acs = localAcapsStatus();
...
acs->dp.EncUsed = ENCBOTH;  /* acs now a pointer! */

/* for shared access */
linkShared();
...
struct AcapsStatus *SysStat = sharedAcapsStatus();
SysStat->dp.EncUsed = ENCBOTH;  /* just as an example */

3 Consequences

  1. Programs sub and display have been modified (very few lines of code needed to be changed) and compile and link successfully. However, I suggest in the longer run we create an alternative to display, which would consist of a GUI running on munin, and getting telescope status via MQTT.
  2. Programs watchdog, loop and astra are currently being combined into one C++ program, which uses the new CANopen stack. Combining them into one process makes time synchronisation between them a lot easier and more reliable. This is work in progress.
  3. Program cid has also been modified, but the old version made direct access to hardware interfaces. This needs to be decoupled, all access to hardware should be done by the program described under item 2.