a2p2 0.2.15__py3-none-any.whl → 0.7.4__py3-none-any.whl
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.
- a2p2/__main__.py +39 -0
- a2p2/chara/facility.py +51 -3
- a2p2/chara/gui.py +29 -5
- a2p2/client.py +144 -34
- a2p2/facility.py +11 -1
- a2p2/gui.py +32 -1
- a2p2/jmmc/__init__.py +7 -0
- a2p2/jmmc/catalogs.py +129 -0
- a2p2/jmmc/generated_models.py +191 -0
- a2p2/jmmc/models.py +104 -0
- a2p2/jmmc/services.py +16 -0
- a2p2/jmmc/utils.py +130 -0
- a2p2/jmmc/webservices.py +48 -0
- a2p2/ob.py +95 -9
- a2p2/samp.py +17 -0
- a2p2/version.py +205 -137
- a2p2/vlti/conf/GRAVITY_ditTable.json +21 -19
- a2p2/vlti/conf/GRAVITY_rangeTable.json +200 -28
- a2p2/vlti/conf/MATISSE_rangeTable.json +58 -22
- a2p2/vlti/conf/PIONIER_ditTable.json +1 -1
- a2p2/vlti/conf/PIONIER_rangeTable.json +16 -18
- a2p2/vlti/facility.py +156 -118
- a2p2/vlti/gravity.py +243 -311
- a2p2/vlti/gui.py +162 -40
- a2p2/vlti/instrument.py +264 -49
- a2p2/vlti/matisse.py +61 -147
- a2p2/vlti/pionier.py +34 -157
- {a2p2-0.2.15.dist-info → a2p2-0.7.4.dist-info}/METADATA +34 -20
- a2p2-0.7.4.dist-info/RECORD +39 -0
- {a2p2-0.2.15.dist-info → a2p2-0.7.4.dist-info}/WHEEL +1 -1
- {a2p2-0.2.15.dist-info → a2p2-0.7.4.dist-info}/entry_points.txt +0 -1
- a2p2/vlti/confP104/GRAVITY_ditTable.json +0 -122
- a2p2/vlti/confP104/GRAVITY_rangeTable.json +0 -202
- a2p2/vlti/confP104/MATISSE_ditTable.json +0 -2
- a2p2/vlti/confP104/MATISSE_rangeTable.json +0 -202
- a2p2/vlti/confP104/PIONIER_ditTable.json +0 -77
- a2p2/vlti/confP104/PIONIER_rangeTable.json +0 -118
- a2p2/vlti/confP105/GRAVITY_ditTable.json +0 -37
- a2p2/vlti/confP105/GRAVITY_rangeTable.json +0 -42
- a2p2/vlti/confP105/MATISSE_ditTable.json +0 -2
- a2p2/vlti/confP105/MATISSE_rangeTable.json +0 -44
- a2p2/vlti/confP105/PIONIER_ditTable.json +0 -25
- a2p2/vlti/confP105/PIONIER_rangeTable.json +0 -38
- a2p2-0.2.15.dist-info/RECORD +0 -44
- {a2p2-0.2.15.dist-info → a2p2-0.7.4.dist-info}/LICENSE +0 -0
- {a2p2-0.2.15.dist-info → a2p2-0.7.4.dist-info}/top_level.txt +0 -0
    
        a2p2/__main__.py
    CHANGED
    
    | @@ -2,7 +2,40 @@ | |
| 2 2 | 
             
            from __future__ import with_statement
         | 
| 3 3 |  | 
| 4 4 | 
             
            import traceback
         | 
| 5 | 
            +
             | 
| 5 6 | 
             
            from argparse import ArgumentParser
         | 
| 7 | 
            +
            from a2p2 import __release_notes__
         | 
| 8 | 
            +
            from distutils.version import LooseVersion
         | 
| 9 | 
            +
             | 
| 10 | 
            +
             | 
| 11 | 
            +
             | 
| 12 | 
            +
            def createMdReleaseNotes():
         | 
| 13 | 
            +
                """
         | 
| 14 | 
            +
                Present in a reverse ordered way the dictionnary version items.
         | 
| 15 | 
            +
                """
         | 
| 16 | 
            +
                txt = "# A2P2 release notes"
         | 
| 17 | 
            +
                lvs = [LooseVersion(v) for v in __release_notes__]
         | 
| 18 | 
            +
                for lv in sorted(lvs, reverse=True):
         | 
| 19 | 
            +
                    v=lv.vstring
         | 
| 20 | 
            +
                    txt += "\n## V " + v + " :"
         | 
| 21 | 
            +
                    keys = []
         | 
| 22 | 
            +
                    for k in sorted(__release_notes__[v]):
         | 
| 23 | 
            +
                        if not ("TODO" in k):
         | 
| 24 | 
            +
                            keys.append(k)
         | 
| 25 | 
            +
                    for k in sorted(__release_notes__[v]):
         | 
| 26 | 
            +
                        if "TODO" in k:
         | 
| 27 | 
            +
                            keys.append(k)
         | 
| 28 | 
            +
             | 
| 29 | 
            +
                    for e in keys:
         | 
| 30 | 
            +
                        infos = __release_notes__[v][e]
         | 
| 31 | 
            +
                        if infos:
         | 
| 32 | 
            +
                            txt += "\n### " + e + " : "
         | 
| 33 | 
            +
                            for i in infos:
         | 
| 34 | 
            +
                                txt += "\n- " + i
         | 
| 35 | 
            +
                    txt += "\n\n"
         | 
| 36 | 
            +
                f=open("release-notes.md", "w")
         | 
| 37 | 
            +
                f.write(txt)
         | 
| 38 | 
            +
                print(f"{f.name} generated")
         | 
| 6 39 |  | 
| 7 40 |  | 
| 8 41 | 
             
            def main():
         | 
| @@ -14,15 +47,21 @@ def main(): | |
| 14 47 | 
             
                                    help='fake API to avoid remote connection (dev. only).')
         | 
| 15 48 | 
             
                parser.add_argument('-v', '--verbose', action='store_true', help='Verbose')
         | 
| 16 49 | 
             
                parser.add_argument('-c', '--createprefs', action='store_true', help='Create preferences file')
         | 
| 50 | 
            +
                parser.add_argument('-r', '--releasenotes', action='store_true', help='Create md release notes file')
         | 
| 17 51 |  | 
| 18 52 | 
             
                args = parser.parse_args()
         | 
| 19 53 |  | 
| 20 54 | 
             
                from . import A2p2Client
         | 
| 21 55 |  | 
| 56 | 
            +
                if args.releasenotes:
         | 
| 57 | 
            +
                    createMdReleaseNotes()
         | 
| 58 | 
            +
                    exit()
         | 
| 59 | 
            +
             | 
| 22 60 | 
             
                if args.createprefs:
         | 
| 23 61 | 
             
                    A2p2Client.createPreferencesFile()
         | 
| 24 62 | 
             
                    exit()
         | 
| 25 63 |  | 
| 64 | 
            +
             | 
| 26 65 | 
             
                try:
         | 
| 27 66 | 
             
                    with A2p2Client(args.fakeapi, args.verbose) as a2p2c:
         | 
| 28 67 | 
             
                        # if  args.config:
         | 
    
        a2p2/chara/facility.py
    CHANGED
    
    | @@ -4,7 +4,9 @@ __all__ = [] | |
| 4 4 |  | 
| 5 5 | 
             
            from a2p2.chara.gui import CharaUI
         | 
| 6 6 | 
             
            from a2p2.facility import Facility
         | 
| 7 | 
            +
            import requests
         | 
| 7 8 | 
             
            import logging
         | 
| 9 | 
            +
            import traceback
         | 
| 8 10 |  | 
| 9 11 | 
             
            HELPTEXT = "TODO update this HELP message in a2p2/chara/facility.py"
         | 
| 10 12 |  | 
| @@ -15,8 +17,13 @@ class CharaFacility(Facility): | |
| 15 17 | 
             
                def __init__(self, a2p2client):
         | 
| 16 18 | 
             
                    Facility.__init__(self, a2p2client, "CHARA", HELPTEXT)
         | 
| 17 19 | 
             
                    self.charaUI = CharaUI(self)
         | 
| 20 | 
            +
                    self.connected2OB2 = False
         | 
| 21 | 
            +
                    self.validQueueServer = None
         | 
| 18 22 |  | 
| 19 23 | 
             
                def processOB(self, ob):
         | 
| 24 | 
            +
                    if not ob:
         | 
| 25 | 
            +
                        return
         | 
| 26 | 
            +
             | 
| 20 27 | 
             
                    self.a2p2client.ui.addToLog(
         | 
| 21 28 | 
             
                        "OB received for '" + self.facilityName + "' interferometer")
         | 
| 22 29 | 
             
                    # show ob dict for debug
         | 
| @@ -28,8 +35,49 @@ class CharaFacility(Facility): | |
| 28 35 | 
             
                    # give focus on last updated UI
         | 
| 29 36 | 
             
                    self.a2p2client.ui.showFacilityUI(self.charaUI)
         | 
| 30 37 |  | 
| 38 | 
            +
                def checkServer(self):
         | 
| 39 | 
            +
                    if self.validQueueServer:
         | 
| 40 | 
            +
                        # recheck ?
         | 
| 41 | 
            +
                        return True
         | 
| 42 | 
            +
                    else:
         | 
| 43 | 
            +
                        # search and test servers from preferences
         | 
| 44 | 
            +
                        queueServers=self.a2p2client.preferences.getCharaQueueServer()
         | 
| 45 | 
            +
                        msg=""
         | 
| 46 | 
            +
                        if queueServers :
         | 
| 47 | 
            +
                            for queueServer in queueServers:
         | 
| 48 | 
            +
                                logger.debug(f'Trying to send OB on queuserver : {queueServer}')
         | 
| 49 | 
            +
                                try:
         | 
| 50 | 
            +
                                    c=requests.get(queueServer, timeout=3)
         | 
| 51 | 
            +
                                    msg+=f"Connection succeeded on OB server : {c.json()}\n"
         | 
| 52 | 
            +
                                    self.validQueueServer = queueServer
         | 
| 53 | 
            +
                                    break
         | 
| 54 | 
            +
                                except:
         | 
| 55 | 
            +
                                    msg+=f"Connection failed on {queueServer}\n"
         | 
| 56 | 
            +
             | 
| 57 | 
            +
                        self.a2p2client.ui.addToLog(msg)
         | 
| 58 | 
            +
                        self.charaUI.display(msg)
         | 
| 59 | 
            +
             | 
| 60 | 
            +
                        return self.validQueueServer
         | 
| 61 | 
            +
             | 
| 31 62 | 
             
                def consumeOB(self, ob):
         | 
| 32 | 
            -
                     | 
| 33 | 
            -
             | 
| 34 | 
            -
             | 
| 63 | 
            +
                    if self.checkServer():
         | 
| 64 | 
            +
                        try:
         | 
| 65 | 
            +
                            r = requests.post(self.validQueueServer, json=ob.as_dict(), timeout=3)
         | 
| 66 | 
            +
                            msg = ""
         | 
| 67 | 
            +
                            msg+=f"OB sent to remote server queue : {r}"
         | 
| 68 | 
            +
                            self.connected2OB2 = True
         | 
| 69 | 
            +
                            self.a2p2client.ui.addToLog(msg)
         | 
| 70 | 
            +
                            self.charaUI.display(msg)
         | 
| 71 | 
            +
                        except:
         | 
| 72 | 
            +
                            print(traceback.format_exc())
         | 
| 73 | 
            +
                            msg=f"Can't send OB to the '{self.validQueueServer}' queue server, please relaunch it or try again for a new one."
         | 
| 74 | 
            +
                            self.validQueueServer = None
         | 
| 75 | 
            +
                            self.a2p2client.ui.addToLog(msg)
         | 
| 76 | 
            +
                            self.charaUI.display(msg)
         | 
| 77 | 
            +
                    else:
         | 
| 78 | 
            +
                        msg=f"Can't find any queue server, please launch it, edit your preferences or check your ssh port forwarding"
         | 
| 79 | 
            +
             | 
| 80 | 
            +
                    # display OB
         | 
| 35 81 | 
             
                    self.charaUI.displayOB(ob)
         | 
| 82 | 
            +
             | 
| 83 | 
            +
             | 
    
        a2p2/chara/gui.py
    CHANGED
    
    | @@ -42,6 +42,10 @@ class CharaUI(FacilityUI): | |
| 42 42 | 
             
                    else:
         | 
| 43 43 | 
             
                        return None
         | 
| 44 44 |  | 
| 45 | 
            +
                def display(self, msg):
         | 
| 46 | 
            +
                    self.text.insert(END, msg)
         | 
| 47 | 
            +
             | 
| 48 | 
            +
             | 
| 45 49 | 
             
                def displayOB(self, ob):
         | 
| 46 50 | 
             
                    try:
         | 
| 47 51 | 
             
                        buffer = self.extractReport(ob)
         | 
| @@ -91,8 +95,8 @@ AO Flat Star: | |
| 91 95 | 
             
                    targets = {}  # store  ids for futur retrieval in schedule
         | 
| 92 96 | 
             
                    for oc in ob.observationConfiguration:
         | 
| 93 97 | 
             
                        targets[oc.id] = oc
         | 
| 94 | 
            -
                        if "SCI" in oc.type:
         | 
| 95 | 
            -
             | 
| 98 | 
            +
                        # if "SCI" in oc.type:
         | 
| 99 | 
            +
                        sciences.append(oc)
         | 
| 96 100 |  | 
| 97 101 | 
             
                    # Retrieve cals from schedule
         | 
| 98 102 | 
             
                    cals = {}
         | 
| @@ -109,13 +113,28 @@ AO Flat Star: | |
| 109 113 |  | 
| 110 114 | 
             
                    for oc in sciences:
         | 
| 111 115 | 
             
                        sct = oc.SCTarget
         | 
| 116 | 
            +
                        extrainfos = self.get(sct, "EXTRA_INFORMATIONS")
         | 
| 112 117 | 
             
                        ftt = self.get(oc, "FTTarget")
         | 
| 113 118 | 
             
                        aot = self.get(oc, "AOTarget")
         | 
| 114 | 
            -
             | 
| 115 | 
            -
                         | 
| 119 | 
            +
             | 
| 120 | 
            +
                        if self.get(oc,'observationConstraints'):
         | 
| 121 | 
            +
                            buffer += ", ".join(oc.observationConstraints.LSTinterval) + "\n"
         | 
| 122 | 
            +
                        else:
         | 
| 123 | 
            +
                            buffer += "                   NOT OBSERVABLE \n"
         | 
| 124 | 
            +
                        buffer += f"{oc.type} Object:\n"
         | 
| 116 125 | 
             
                        fluxes = ", ".join([e[0] + "=" + e[1]
         | 
| 117 126 | 
             
                                            for e in ob.getFluxes(sct).items()])
         | 
| 118 | 
            -
                         | 
| 127 | 
            +
                        try:
         | 
| 128 | 
            +
                            info = sct.SPECTYP
         | 
| 129 | 
            +
                        except:
         | 
| 130 | 
            +
                            info = "no SPECTYP"
         | 
| 131 | 
            +
                        info+=", "
         | 
| 132 | 
            +
                        try:
         | 
| 133 | 
            +
                            info += sct.PARALLAX
         | 
| 134 | 
            +
                        except:
         | 
| 135 | 
            +
                            info += "no PARALLAX"
         | 
| 136 | 
            +
             | 
| 137 | 
            +
             | 
| 119 138 | 
             
                        buffer += sct.name + " (" + info + ") : " + fluxes + "\n"
         | 
| 120 139 | 
             
                        if ftt:
         | 
| 121 140 | 
             
                            buffer += "Fringe Finder:\n"
         | 
| @@ -133,6 +152,11 @@ AO Flat Star: | |
| 133 152 | 
             
                            for cal in cals:
         | 
| 134 153 | 
             
                                buffer += "- " + cal + "\n"
         | 
| 135 154 |  | 
| 155 | 
            +
                        # Display Extra_Informations if any
         | 
| 156 | 
            +
                        if extrainfos:
         | 
| 157 | 
            +
                            buffer += "Extra_infos:\n"
         | 
| 158 | 
            +
                            buffer += ", ".join([f"{field}={self.get(extrainfos,field)}" for field in extrainfos._fields])
         | 
| 159 | 
            +
             | 
| 136 160 | 
             
                        buffer += _HR
         | 
| 137 161 |  | 
| 138 162 | 
             
                    return buffer
         | 
    
        a2p2/client.py
    CHANGED
    
    | @@ -16,19 +16,23 @@ from a2p2.samp import A2p2SampClient | |
| 16 16 | 
             
            from a2p2.vlti.facility import VltiFacility
         | 
| 17 17 |  | 
| 18 18 |  | 
| 19 | 
            +
             | 
| 19 20 | 
             
            # prepare global logging
         | 
| 20 21 | 
             
            a2p2Rootlogger = logging.getLogger("a2p2")
         | 
| 21 | 
            -
            # uncomment next line to log requests done by p2api and maybe other ones...
         | 
| 22 | 
            -
            #a2p2Rootlogger = logging.getLogger()
         | 
| 23 22 | 
             
            a2p2Rootlogger.setLevel(logging.INFO)
         | 
| 23 | 
            +
            # uncomment next two lines to log requests done by p2api as debug and maybe other ones...
         | 
| 24 | 
            +
            #a2p2Rootlogger = logging.getLogger()
         | 
| 25 | 
            +
            #a2p2Rootlogger.setLevel(logging.DEBUG)
         | 
| 24 26 | 
             
            console = logging.StreamHandler()
         | 
| 25 27 | 
             
            console.setLevel(logging.DEBUG)
         | 
| 26 | 
            -
            consoleFormatter = logging.Formatter( | 
| 28 | 
            +
            consoleFormatter = logging.Formatter(
         | 
| 29 | 
            +
                '%(levelname)s - %(name)s  - %(asctime)s - %(filename)s:%(lineno)d - %(message)s')
         | 
| 27 30 | 
             
            console.setFormatter(consoleFormatter)
         | 
| 28 31 | 
             
            a2p2Rootlogger.addHandler(console)
         | 
| 29 32 |  | 
| 30 33 | 
             
            logger = logging.getLogger(__name__)
         | 
| 31 34 |  | 
| 35 | 
            +
             | 
| 32 36 | 
             
            class A2p2Client():
         | 
| 33 37 | 
             
                """Transmit your Aspro2 observation to remote Observatory scheduling database.
         | 
| 34 38 |  | 
| @@ -36,7 +40,6 @@ class A2p2Client(): | |
| 36 40 | 
             
                       a2p2.run()
         | 
| 37 41 | 
             
                       ..."""
         | 
| 38 42 |  | 
| 39 | 
            -
             | 
| 40 43 | 
             
                def __init__(self, fakeAPI=False, verbose=False):
         | 
| 41 44 | 
             
                    """Create the A2p2 client."""
         | 
| 42 45 |  | 
| @@ -56,11 +59,17 @@ class A2p2Client(): | |
| 56 59 | 
             
                    self.facilityManager = FacilityManager(self)
         | 
| 57 60 |  | 
| 58 61 | 
             
                    if self.preferences.exists():
         | 
| 59 | 
            -
                         | 
| 62 | 
            +
                        A2P2ClientPreferences.updatePreferencesFile()
         | 
| 63 | 
            +
                        self.ui.addToLog(
         | 
| 64 | 
            +
                            f"Using preference from '{A2P2ClientPreferences.getPreferencesFileName()}'.\n")
         | 
| 60 65 | 
             
                    else:
         | 
| 61 | 
            -
                        self.ui.addToLog( | 
| 66 | 
            +
                        self.ui.addToLog(
         | 
| 67 | 
            +
                            "No preference file found, please create one so your data persists (launch program with -c option).\n")
         | 
| 62 68 |  | 
| 63 | 
            -
                    self. | 
| 69 | 
            +
                    self.ui.addToLog("Please often update ( pip install -U [--user] a2p2 ) and don't hesitate to send any feedback or issues!\n\n")
         | 
| 70 | 
            +
             | 
| 71 | 
            +
             | 
| 72 | 
            +
                    self.errors = []
         | 
| 64 73 |  | 
| 65 74 | 
             
                    pass
         | 
| 66 75 |  | 
| @@ -120,16 +129,16 @@ class A2p2Client(): | |
| 120 129 |  | 
| 121 130 | 
             
                    # handle autologin
         | 
| 122 131 | 
             
                    if self.preferences.getP2AutoLoginBoolean():
         | 
| 123 | 
            -
                        logger.debug("Autologin using '%s' file"% | 
| 124 | 
            -
             | 
| 132 | 
            +
                        logger.debug("Autologin using '%s' file" %
         | 
| 133 | 
            +
                                     A2P2ClientPreferences.getPreferencesFileName())
         | 
| 125 134 | 
             
                        self.ui.loop()
         | 
| 126 | 
            -
                        vltifacility = self.facilityManager.facilities.get( | 
| 135 | 
            +
                        vltifacility = self.facilityManager.facilities.get(
         | 
| 136 | 
            +
                            VltiFacility.getName())
         | 
| 127 137 | 
             
                        vltifacility.autologin()
         | 
| 128 138 |  | 
| 129 | 
            -
             | 
| 130 139 | 
             
                    # We now run the loop to wait for the message in a try/finally block so that if
         | 
| 131 140 | 
             
                    # the program is interrupted e.g. by control-C, the client terminates
         | 
| 132 | 
            -
                    # gracefully.
         | 
| 141 | 
            +
                    # gracefully. ( but sometimes fails inside tkinter/__init__.py CallWrapper/__call__() )
         | 
| 133 142 |  | 
| 134 143 | 
             
                    # We test every 1s to see if the hub has sent a message
         | 
| 135 144 | 
             
                    delay = 0.1
         | 
| @@ -142,8 +151,14 @@ class A2p2Client(): | |
| 142 151 | 
             
                            loop_cnt += 1
         | 
| 143 152 | 
             
                            time.sleep(delay)
         | 
| 144 153 |  | 
| 154 | 
            +
            #                if loop_cnt % each == 0:
         | 
| 155 | 
            +
            #                    logger.debug(f"loop {loop_cnt}")
         | 
| 156 | 
            +
             | 
| 145 157 | 
             
                            self.ui.loop()
         | 
| 146 158 |  | 
| 159 | 
            +
                            # process any stacked OBs
         | 
| 160 | 
            +
                            self.facilityManager.processOB()
         | 
| 161 | 
            +
             | 
| 147 162 | 
             
                            if not self.a2p2SampClient.is_connected() and loop_cnt % each == 0:
         | 
| 148 163 | 
             
                                try:
         | 
| 149 164 | 
             
                                    self.a2p2SampClient.connect()
         | 
| @@ -159,11 +174,21 @@ class A2p2Client(): | |
| 159 174 |  | 
| 160 175 | 
             
                            if self.a2p2SampClient.has_message():
         | 
| 161 176 | 
             
                                try:
         | 
| 162 | 
            -
                                     | 
| 163 | 
            -
             | 
| 177 | 
            +
                                    if self.a2p2SampClient.has_ob_message():
         | 
| 178 | 
            +
                                        ob = OB(self.a2p2SampClient.get_ob_url())
         | 
| 179 | 
            +
                                        self.facilityManager.processOB(ob)
         | 
| 164 180 | 
             
                                except:
         | 
| 165 181 | 
             
                                    self.ui.addToLog(
         | 
| 166 | 
            -
                                        "Exception during ob creation: " + traceback.format_exc(), False)
         | 
| 182 | 
            +
                                        "Exception during a2p2 ob creation: " + traceback.format_exc(), False)
         | 
| 183 | 
            +
                                    self.ui.addToLog("Can't process last OB")
         | 
| 184 | 
            +
             | 
| 185 | 
            +
                                try:
         | 
| 186 | 
            +
                                    if self.a2p2SampClient.has_model_message():
         | 
| 187 | 
            +
                                            m = self.a2p2SampClient.get_model()
         | 
| 188 | 
            +
                                            self.showModel(m)
         | 
| 189 | 
            +
                                except:
         | 
| 190 | 
            +
                                    self.ui.addToLog(
         | 
| 191 | 
            +
                                        "Exception during a2p2 ob creation: " + traceback.format_exc(), False)
         | 
| 167 192 | 
             
                                    self.ui.addToLog("Can't process last OB")
         | 
| 168 193 |  | 
| 169 194 | 
             
                                # always clear previous received message
         | 
| @@ -173,16 +198,27 @@ class A2p2Client(): | |
| 173 198 | 
             
                                loop_cnt = -1
         | 
| 174 199 | 
             
                        except KeyboardInterrupt:
         | 
| 175 200 | 
             
                            loop_cnt = -1
         | 
| 201 | 
            +
                    print("\nDisconnecting SAMP ...")
         | 
| 202 | 
            +
                    self.a2p2SampClient.disconnect()
         | 
| 203 | 
            +
                    print("Bye!")
         | 
| 204 | 
            +
             | 
| 176 205 |  | 
| 177 206 | 
             
                def createPreferencesFile():
         | 
| 178 207 | 
             
                    A2P2ClientPreferences.createPreferencesFile()
         | 
| 179 208 |  | 
| 209 | 
            +
             | 
| 210 | 
            +
                def showModel(self, xmlmodel):
         | 
| 211 | 
            +
                    from a2p2.jmmc.models import modelsFromXml
         | 
| 212 | 
            +
                    self.ui.addToLog("Received star model")
         | 
| 213 | 
            +
                    self.ui.addToLog(modelsFromXml(xmlmodel))
         | 
| 214 | 
            +
             | 
| 215 | 
            +
             | 
| 180 216 | 
             
            class A2P2ClientPreferences():
         | 
| 181 217 | 
             
                # define application name
         | 
| 182 218 | 
             
                appname = "a2p2"
         | 
| 183 219 |  | 
| 184 220 | 
             
                def __init__(self):
         | 
| 185 | 
            -
                    self._config = | 
| 221 | 
            +
                    self._config = A2P2ClientPreferences.getPreferences()
         | 
| 186 222 | 
             
                    pass
         | 
| 187 223 |  | 
| 188 224 | 
             
                def exists(self):
         | 
| @@ -193,36 +229,84 @@ class A2P2ClientPreferences(): | |
| 193 229 |  | 
| 194 230 | 
             
                def getPreferences():
         | 
| 195 231 | 
             
                    preferences_file = A2P2ClientPreferences.getPreferencesFileName()
         | 
| 196 | 
            -
                    config = configparser.ConfigParser()
         | 
| 232 | 
            +
                    config = configparser.ConfigParser(allow_no_value=True)
         | 
| 197 233 | 
             
                    config.read(preferences_file)
         | 
| 198 234 | 
             
                    return config
         | 
| 199 235 |  | 
| 200 236 | 
             
                def getPreferencesFileName():
         | 
| 201 237 | 
             
                    from appdirs import user_config_dir
         | 
| 202 | 
            -
                    preferences_file = os.path.join(user_config_dir( | 
| 238 | 
            +
                    preferences_file = os.path.join(user_config_dir(
         | 
| 239 | 
            +
                        A2P2ClientPreferences.appname), "prefs.ini")
         | 
| 203 240 | 
             
                    return preferences_file
         | 
| 241 | 
            +
                def updatePreferencesFile():
         | 
| 242 | 
            +
                    return
         | 
| 243 | 
            +
                    # TODO
         | 
| 244 | 
            +
                    # we suppose the file exists
         | 
| 245 | 
            +
                    filename = A2P2ClientPreferences.getPreferencesFileName()
         | 
| 246 | 
            +
                    buffer=""
         | 
| 247 | 
            +
                    for line in open(filename):
         | 
| 248 | 
            +
                        buffer+=line+"\n"
         | 
| 249 | 
            +
                    print (buffer)
         | 
| 204 250 |  | 
| 205 251 | 
             
                def createPreferencesFile():
         | 
| 206 | 
            -
                    filename=A2P2ClientPreferences.getPreferencesFileName()
         | 
| 207 | 
            -
             | 
| 252 | 
            +
                    filename = A2P2ClientPreferences.getPreferencesFileName()
         | 
| 208 253 | 
             
                    if os.path.exists(filename):
         | 
| 209 | 
            -
                        print(" | 
| 254 | 
            +
                        print(f"{filename} already exists. Nothing done")
         | 
| 255 | 
            +
                        p=A2P2ClientPreferences()
         | 
| 256 | 
            +
                        versionInPref=p.getA2P2Version()
         | 
| 257 | 
            +
                        if __version__ != versionInPref:
         | 
| 258 | 
            +
                            print(f"HINT: You may try to backup this file (V{versionInPref}) and merge with a new generated one for V{__version__}.")
         | 
| 210 259 | 
             
                    else:
         | 
| 211 | 
            -
                         | 
| 260 | 
            +
                        import getpass
         | 
| 261 | 
            +
             | 
| 262 | 
            +
                        config = configparser.ConfigParser(allow_no_value=True)
         | 
| 212 263 | 
             
                        #config['DEFAULT'] = {'_noprefyet': '42'}
         | 
| 264 | 
            +
             | 
| 265 | 
            +
                        config['a2p2'] = {}
         | 
| 266 | 
            +
                        s = config['a2p2']
         | 
| 267 | 
            +
                        s['# A2P2 SECTION'] = ""
         | 
| 268 | 
            +
                        s['# = > please do not modify next properties <'] = ""
         | 
| 269 | 
            +
                        s['version'] = __version__
         | 
| 270 | 
            +
             | 
| 271 | 
            +
             | 
| 272 | 
            +
                        config['jmmc'] = {}
         | 
| 273 | 
            +
                        s = config['jmmc']
         | 
| 274 | 
            +
                        s['# JMMC SECTION'] = ""
         | 
| 275 | 
            +
                        s['# = > please uncomment and update next properties to make it active <'] = ""
         | 
| 276 | 
            +
                        s['#login'] = "my.email@my.lab"
         | 
| 277 | 
            +
                        s['#password'] = "12345zZ"
         | 
| 278 | 
            +
             | 
| 279 | 
            +
                        config['chara'] = {}
         | 
| 280 | 
            +
                        s = config['chara']
         | 
| 281 | 
            +
                        s['# CHARA SECTION'] = ""
         | 
| 282 | 
            +
                        s['# = > please uncomment and update next properties to make it active <'] = ""
         | 
| 283 | 
            +
                        s['# = > queueserver may be uncommented to forward OB to a remote server instead <'] = ""
         | 
| 284 | 
            +
                        s['#queueserver'] = 'http://192.168.3.153:2468/test,http://localhost:2468/test'
         | 
| 285 | 
            +
             | 
| 213 286 | 
             
                        config['p2'] = {}
         | 
| 214 | 
            -
                         | 
| 215 | 
            -
                         | 
| 216 | 
            -
                         | 
| 217 | 
            -
                         | 
| 218 | 
            -
                         | 
| 219 | 
            -
                         | 
| 287 | 
            +
                        s=config['p2']
         | 
| 288 | 
            +
                        s['# ESO P2 SECTION'] = ""
         | 
| 289 | 
            +
                        s['# = > please uncomment and update next properties to make it active <'] = ""
         | 
| 290 | 
            +
                        s['#username'] = getpass.getuser()
         | 
| 291 | 
            +
                        s['#password'] = "12345zZ"
         | 
| 292 | 
            +
                        s['#autologin'] = "yes"
         | 
| 293 | 
            +
                        s['# = > please change next properties to change the name recorded in the OB comments <'] = ""
         | 
| 294 | 
            +
                        s['user_comment_name'] = f"{getpass.getuser()}"
         | 
| 295 | 
            +
             | 
| 296 | 
            +
                        config['p2.iss.vltitype'] = {}
         | 
| 297 | 
            +
                        s=config['p2.iss.vltitype']
         | 
| 298 | 
            +
                        s['# = > please uncomment the default values to add for ISS.VLTITYPE <'] = ""
         | 
| 299 | 
            +
                        s['# = > all supported will be added for any VLTI instrument if info is not provided by Aspro2 <'] = None
         | 
| 300 | 
            +
                        s['#snapshot'] = None
         | 
| 301 | 
            +
                        s['#imaging'] = None
         | 
| 302 | 
            +
                        s['#time-series'] = None
         | 
| 303 | 
            +
                        s['#astrometry'] = None
         | 
| 220 304 |  | 
| 221 305 | 
             
                        os.makedirs(os.path.dirname(filename), exist_ok=True)
         | 
| 222 306 | 
             
                        with open(filename, 'w+') as configfile:
         | 
| 223 307 | 
             
                            config.write(configfile)
         | 
| 224 308 |  | 
| 225 | 
            -
                        print("%s template created. Please adjust."%filename)
         | 
| 309 | 
            +
                        print("'%s' template file created to store preferences. Please check and adjust it's content." % filename)
         | 
| 226 310 |  | 
| 227 311 | 
             
                def getConfig(self, section, key, default=None):
         | 
| 228 312 | 
             
                    try:
         | 
| @@ -230,19 +314,44 @@ class A2P2ClientPreferences(): | |
| 230 314 | 
             
                    except:
         | 
| 231 315 | 
             
                        return default
         | 
| 232 316 |  | 
| 317 | 
            +
                def getConfigKeys(self, section, default=None):
         | 
| 318 | 
            +
                    try:
         | 
| 319 | 
            +
                        return list(self._config[section].keys())
         | 
| 320 | 
            +
                    except:
         | 
| 321 | 
            +
                        return default
         | 
| 322 | 
            +
             | 
| 323 | 
            +
             | 
| 233 324 | 
             
                def getConfigBoolean(self, section, key, default=None):
         | 
| 234 325 | 
             
                    try:
         | 
| 235 | 
            -
                        return self._config.getboolean(section,key)
         | 
| 326 | 
            +
                        return self._config.getboolean(section, key)
         | 
| 236 327 | 
             
                    except:
         | 
| 237 328 | 
             
                        return default
         | 
| 238 329 |  | 
| 330 | 
            +
                # Retrieve A2P2 prefs
         | 
| 331 | 
            +
                def getA2P2Version(self):
         | 
| 332 | 
            +
                    return self.getConfig("a2p2", "version", 'missing')
         | 
| 333 | 
            +
             | 
| 334 | 
            +
                # Retrieve JMMC prefs
         | 
| 335 | 
            +
                def getJmmcLogin(self):
         | 
| 336 | 
            +
                    return self.getConfig("jmmc", "login", None)
         | 
| 239 337 |  | 
| 240 | 
            -
                 | 
| 338 | 
            +
                def getJmmcPassword(self):
         | 
| 339 | 
            +
                    return self.getConfig("jmmc", "password", None)
         | 
| 340 | 
            +
             | 
| 341 | 
            +
                # Retrieve CHARA prefs
         | 
| 342 | 
            +
                def getCharaQueueServer(self):
         | 
| 343 | 
            +
                    s = self.getConfig("chara", "queueserver", None)
         | 
| 344 | 
            +
                    if s:
         | 
| 345 | 
            +
                        return s.split(",")
         | 
| 346 | 
            +
             | 
| 347 | 
            +
             | 
| 348 | 
            +
                # Retrieve P2 prefs
         | 
| 241 349 | 
             
                def getP2Username(self):
         | 
| 242 | 
            -
                     | 
| 350 | 
            +
                    """ Get P2 username in config or use default demo account """
         | 
| 351 | 
            +
                    return self.getConfig("p2", "username", '52052')
         | 
| 243 352 |  | 
| 244 | 
            -
                # retrieve P2 password in config or use default demo account
         | 
| 245 353 | 
             
                def getP2Password(self):
         | 
| 354 | 
            +
                    """Get P2 password in config or use default demo account"""
         | 
| 246 355 | 
             
                    return self.getConfig("p2", "password", 'tutorial')
         | 
| 247 356 |  | 
| 248 357 | 
             
                def getP2UserCommentName(self):
         | 
| @@ -250,5 +359,6 @@ class A2P2ClientPreferences(): | |
| 250 359 | 
             
                    return self.getConfig("p2", "user_comment_name", getpass.getuser())
         | 
| 251 360 |  | 
| 252 361 | 
             
                def getP2AutoLoginBoolean(self):
         | 
| 253 | 
            -
             | 
| 362 | 
            +
                    return self.getConfigBoolean("p2", "autologin", False)
         | 
| 363 | 
            +
             | 
| 254 364 |  | 
    
        a2p2/facility.py
    CHANGED
    
    | @@ -38,7 +38,17 @@ class FacilityManager(): | |
| 38 38 |  | 
| 39 39 | 
             
                    return " | ".join(status)
         | 
| 40 40 |  | 
| 41 | 
            -
                def processOB(self, ob):
         | 
| 41 | 
            +
                def processOB(self, ob=None):
         | 
| 42 | 
            +
             | 
| 43 | 
            +
                    # handled queued obs if ob is null
         | 
| 44 | 
            +
                    # TODO add a new method in the futur
         | 
| 45 | 
            +
                    if not ob:
         | 
| 46 | 
            +
                        for f in self.facilities.values():
         | 
| 47 | 
            +
                            f.processOB(ob)
         | 
| 48 | 
            +
                        return
         | 
| 49 | 
            +
             | 
| 50 | 
            +
                    # else handle given ob
         | 
| 51 | 
            +
             | 
| 42 52 | 
             
                    """ Test instrument on facility that registerInstrument() before OB forward for specialized handling."""
         | 
| 43 53 | 
             
                    interferometer = ob.interferometerConfiguration.name
         | 
| 44 54 | 
             
                    insname = ob.instrumentConfiguration.name
         | 
    
        a2p2/gui.py
    CHANGED
    
    | @@ -19,6 +19,8 @@ else: | |
| 19 19 |  | 
| 20 20 | 
             
            from distutils.version import LooseVersion
         | 
| 21 21 |  | 
| 22 | 
            +
            import signal
         | 
| 23 | 
            +
             | 
| 22 24 | 
             
            HELPTEXT = """This application provides the link between ASPRO (that you should have started) and interferometers facilities.
         | 
| 23 25 |  | 
| 24 26 | 
             
            """
         | 
| @@ -44,6 +46,8 @@ class MainWindow(): | |
| 44 46 | 
             
                        print("Can not set tk scaling !")
         | 
| 45 47 |  | 
| 46 48 | 
             
                    self.window.protocol("WM_DELETE_WINDOW", self._requestAbort)
         | 
| 49 | 
            +
                    # throws an exception self.window.bind_all('<Control-c>', self._requestAbort) # maybe already bound with WM_DELETE_WINDOW ?
         | 
| 50 | 
            +
                    signal.signal(signal.SIGINT, self._requestAbortSignal)
         | 
| 47 51 |  | 
| 48 52 | 
             
                    self.notebook = ttk.Notebook(self.window)
         | 
| 49 53 |  | 
| @@ -96,6 +100,10 @@ class MainWindow(): | |
| 96 100 | 
             
                def _requestAbort(self):
         | 
| 97 101 | 
             
                    self.requestAbort = True
         | 
| 98 102 |  | 
| 103 | 
            +
                def _requestAbortSignal(self, arg1,arg2):
         | 
| 104 | 
            +
                    self.requestAbort = True
         | 
| 105 | 
            +
             | 
| 106 | 
            +
             | 
| 99 107 | 
             
                def addHelp(self, tabname, txt):
         | 
| 100 108 | 
             
                    frame = Frame(self.helptabs)
         | 
| 101 109 | 
             
                    widget = Text(frame, width=120)
         | 
| @@ -176,8 +184,12 @@ class MainWindow(): | |
| 176 184 | 
             
                    self.logtext.insert(END, "\n" + str(text))
         | 
| 177 185 | 
             
                    self.logtext.see(END)
         | 
| 178 186 | 
             
                    self.showFrameToFront()
         | 
| 187 | 
            +
                    self.refresh()
         | 
| 179 188 | 
             
                    logger.log(level,text)
         | 
| 180 189 |  | 
| 190 | 
            +
                def refresh(self):
         | 
| 191 | 
            +
                    self.window.update()
         | 
| 192 | 
            +
             | 
| 181 193 | 
             
                def ShowErrorMessage(self, text):
         | 
| 182 194 | 
             
                    if self.a2p2client.fakeAPI:
         | 
| 183 195 | 
             
                        pass
         | 
| @@ -195,7 +207,7 @@ class MainWindow(): | |
| 195 207 | 
             
                    else:
         | 
| 196 208 | 
             
                        showwarning("Warning", text)
         | 
| 197 209 | 
             
                    self.addToLog("Warning message")
         | 
| 198 | 
            -
                    self.addToLog(text, False,  | 
| 210 | 
            +
                    self.addToLog(text, False, logging.WARNING)
         | 
| 199 211 |  | 
| 200 212 | 
             
                def ShowInfoMessage(self, text):
         | 
| 201 213 | 
             
                    if self.a2p2client.fakeAPI:
         | 
| @@ -205,6 +217,19 @@ class MainWindow(): | |
| 205 217 | 
             
                    self.addToLog("Info message")
         | 
| 206 218 | 
             
                    self.addToLog(text, False, logging.INFO)
         | 
| 207 219 |  | 
| 220 | 
            +
                def AskYesNoMessage(self, text):
         | 
| 221 | 
            +
                    if self.a2p2client.fakeAPI:
         | 
| 222 | 
            +
                        pass
         | 
| 223 | 
            +
                    else:
         | 
| 224 | 
            +
                        self.addToLog(f"Question: {text}", displayString=False)
         | 
| 225 | 
            +
                        ret = askyesno("", text)
         | 
| 226 | 
            +
                        self.addToLog(f"Reply: yes={ret}", displayString=False)
         | 
| 227 | 
            +
                        return ret
         | 
| 228 | 
            +
             | 
| 229 | 
            +
                def addProgress(self, offset=0.1):
         | 
| 230 | 
            +
                    self.setProgress(self.progress_value.get()+offset)
         | 
| 231 | 
            +
             | 
| 232 | 
            +
             | 
| 208 233 | 
             
                def setProgress(self, perc):
         | 
| 209 234 | 
             
                    if perc > 1:
         | 
| 210 235 | 
             
                        perc = perc / 100.0
         | 
| @@ -268,6 +293,12 @@ class FacilityUI(Frame): | |
| 268 293 | 
             
                def ShowInfoMessage(self, *args, **kwargs):
         | 
| 269 294 | 
             
                    self.a2p2client.ui.ShowInfoMessage(*args, **kwargs)
         | 
| 270 295 |  | 
| 296 | 
            +
                def AskYesNoMessage(self,*args, **kwargs):
         | 
| 297 | 
            +
                    return self.a2p2client.ui.AskYesNoMessage(*args, **kwargs)
         | 
| 298 | 
            +
             | 
| 299 | 
            +
                def addProgress(self, offset=0.1):
         | 
| 300 | 
            +
                    self.a2p2client.ui.addProgress(offset)
         | 
| 301 | 
            +
             | 
| 271 302 | 
             
                def setProgress(self, perc):
         | 
| 272 303 | 
             
                    """ Wrapper to update progress bar """
         | 
| 273 304 | 
             
                    if perc > 1:
         |