From 4783e5b56cb853095c4e4ccc45c1ea6d55783710 Mon Sep 17 00:00:00 2001 From: Etienne Pallier Date: Fri, 20 Sep 2019 17:50:13 +0200 Subject: [PATCH] GROS RENOMMAGE des chemins des modules des devices controller... --- .gitignore | 1 + HOWTO_TEST.txt | 4 +++- pyros.py | 4 +++- src/core/pyros_django/agent/Agent.py | 37 +++++++++++++++++++++++++++++++------ src/core/pyros_django/agent/AgentDevice.py | 6 ++++-- src/core/pyros_django/agent/AgentDeviceSBIG.py | 16 ++++++++-------- src/core/pyros_django/agent/AgentDeviceTelescopeGemini.py | 6 +++--- src/core/pyros_django/agent/AgentMultiRequester.py | 268 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/core/pyros_django/majordome/doc/AgentMajordome_object_diag.pu | 10 +++++----- src/device_controller/.gitignore | 2 ++ src/device_controller/README.md | 337 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/device_controller/__init__.py | 1 + src/device_controller/abstract_component/base.py | 564 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/device_controller/abstract_component/detector_sensor.py | 78 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/device_controller/abstract_component/detector_shutter.py | 78 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/device_controller/abstract_component/filter_selector.py | 78 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/device_controller/abstract_component/plc.py | 133 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/device_controller/abstract_component/telescope.py | 569 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/device_controller/channels/client_channel.py | 118 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/device_controller/channels/client_channel_serial.py | 51 +++++++++++++++++++++++++++++++++++++++++++++++++++ src/device_controller/channels/client_channel_socket.py | 181 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/device_controller/channels/client_channel_usb.py | 51 +++++++++++++++++++++++++++++++++++++++++++++++++++ src/device_controller/concrete_component/ak/.emptyfile | 0 src/device_controller/concrete_component/ak/ak_plc_controller.py | 259 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/device_controller/concrete_component/device_simulator_common/server_udp_or_tcp.py | 206 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/device_controller/concrete_component/gemini/.emptyfile | 0 src/device_controller/concrete_component/gemini/client_telescope_gemini_controller_run.py | 270 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/device_controller/concrete_component/gemini/gemini_telescope_controller.py | 286 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/device_controller/concrete_component/gemini/gemini_telescope_simulator.py | 40 ++++++++++++++++++++++++++++++++++++++++ src/device_controller/concrete_component/gemini/server_telescope_gemini_simulator_run.py | 18 ++++++++++++++++++ src/device_controller/concrete_component/sbig/sbig_controller.py | 299 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/device_controller/concrete_component/sbig/sbig_simulator.py | 39 +++++++++++++++++++++++++++++++++++++++ src/device_controller/doc/Device_controller_class_diag.pu | 153 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/device_controller/doc/Device_controller_class_diag_multiinheritance_OLD.pu | 58 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/device_controller/doc/generate_diagrams.sh | 5 +++++ src/device_controller/logs.py | 77 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/device_controller/test/.gitignore | 1 + src/device_controller/test/test_client_gemini.py | 78 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/devices_controller/.gitignore | 2 -- src/devices_controller/README.md | 337 ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- src/devices_controller/__init__.py | 1 - src/devices_controller/channels/client_channel.py | 118 ---------------------------------------------------------------------------------------------------------------------- src/devices_controller/channels/client_channel_serial.py | 51 --------------------------------------------------- src/devices_controller/channels/client_channel_socket.py | 181 ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- src/devices_controller/channels/client_channel_usb.py | 51 --------------------------------------------------- src/devices_controller/devices_controller_abstract_component/device_controller_abstract.py | 563 ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- src/devices_controller/devices_controller_abstract_component/device_controller_detector_sensor.py | 78 ------------------------------------------------------------------------------ src/devices_controller/devices_controller_abstract_component/device_controller_detector_shutter.py | 78 ------------------------------------------------------------------------------ src/devices_controller/devices_controller_abstract_component/device_controller_filter_selector.py | 78 ------------------------------------------------------------------------------ src/devices_controller/devices_controller_abstract_component/device_controller_plc.py | 133 ------------------------------------------------------------------------------------------------------------------------------------- src/devices_controller/devices_controller_abstract_component/device_controller_telescope.py | 569 ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- src/devices_controller/devices_controller_concrete/device_controller_AK/.emptyfile | 0 src/devices_controller/devices_controller_concrete/device_controller_AK/device_controller_plc_ak.py | 259 ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- src/devices_controller/devices_controller_concrete/device_controller_Gemini/.emptyfile | 0 src/devices_controller/devices_controller_concrete/device_controller_Gemini/client_telescope_gemini_controller_run.py | 270 ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ src/devices_controller/devices_controller_concrete/device_controller_Gemini/device_controller_telescope_gemini.py | 285 --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- src/devices_controller/devices_controller_concrete/device_controller_Gemini/device_simulator_telescope_gemini.py | 40 ---------------------------------------- src/devices_controller/devices_controller_concrete/device_controller_Gemini/server_telescope_gemini_simulator_run.py | 18 ------------------ src/devices_controller/devices_controller_concrete/device_controller_SBIG/device_controller_SBIG.py | 300 ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ src/devices_controller/devices_controller_concrete/device_controller_SBIG/device_simulator_SBIG.py | 39 --------------------------------------- src/devices_controller/devices_controller_concrete/device_simulator_common/server_udp_or_tcp.py | 206 -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- src/devices_controller/doc/Device_controller_class_diag.pu | 153 --------------------------------------------------------------------------------------------------------------------------------------------------------- src/devices_controller/doc/Device_controller_class_diag_multiinheritance_OLD.pu | 58 ---------------------------------------------------------- src/devices_controller/doc/generate_diagrams.sh | 5 ----- src/devices_controller/logs.py | 77 ----------------------------------------------------------------------------- src/devices_controller/test/.gitignore | 1 - src/devices_controller/test/test_client_gemini.py | 78 ------------------------------------------------------------------------------ 67 files changed, 4356 insertions(+), 4055 deletions(-) create mode 100755 src/core/pyros_django/agent/AgentMultiRequester.py create mode 100644 src/device_controller/.gitignore create mode 100644 src/device_controller/README.md create mode 100644 src/device_controller/__init__.py create mode 100755 src/device_controller/abstract_component/base.py create mode 100755 src/device_controller/abstract_component/detector_sensor.py create mode 100755 src/device_controller/abstract_component/detector_shutter.py create mode 100755 src/device_controller/abstract_component/filter_selector.py create mode 100755 src/device_controller/abstract_component/plc.py create mode 100755 src/device_controller/abstract_component/telescope.py create mode 100755 src/device_controller/channels/client_channel.py create mode 100755 src/device_controller/channels/client_channel_serial.py create mode 100755 src/device_controller/channels/client_channel_socket.py create mode 100755 src/device_controller/channels/client_channel_usb.py create mode 100644 src/device_controller/concrete_component/ak/.emptyfile create mode 100755 src/device_controller/concrete_component/ak/ak_plc_controller.py create mode 100755 src/device_controller/concrete_component/device_simulator_common/server_udp_or_tcp.py create mode 100644 src/device_controller/concrete_component/gemini/.emptyfile create mode 100755 src/device_controller/concrete_component/gemini/client_telescope_gemini_controller_run.py create mode 100755 src/device_controller/concrete_component/gemini/gemini_telescope_controller.py create mode 100755 src/device_controller/concrete_component/gemini/gemini_telescope_simulator.py create mode 100755 src/device_controller/concrete_component/gemini/server_telescope_gemini_simulator_run.py create mode 100755 src/device_controller/concrete_component/sbig/sbig_controller.py create mode 100755 src/device_controller/concrete_component/sbig/sbig_simulator.py create mode 100755 src/device_controller/doc/Device_controller_class_diag.pu create mode 100755 src/device_controller/doc/Device_controller_class_diag_multiinheritance_OLD.pu create mode 100755 src/device_controller/doc/generate_diagrams.sh create mode 100755 src/device_controller/logs.py create mode 100644 src/device_controller/test/.gitignore create mode 100755 src/device_controller/test/test_client_gemini.py delete mode 100644 src/devices_controller/.gitignore delete mode 100644 src/devices_controller/README.md delete mode 100644 src/devices_controller/__init__.py delete mode 100755 src/devices_controller/channels/client_channel.py delete mode 100755 src/devices_controller/channels/client_channel_serial.py delete mode 100755 src/devices_controller/channels/client_channel_socket.py delete mode 100755 src/devices_controller/channels/client_channel_usb.py delete mode 100755 src/devices_controller/devices_controller_abstract_component/device_controller_abstract.py delete mode 100755 src/devices_controller/devices_controller_abstract_component/device_controller_detector_sensor.py delete mode 100755 src/devices_controller/devices_controller_abstract_component/device_controller_detector_shutter.py delete mode 100755 src/devices_controller/devices_controller_abstract_component/device_controller_filter_selector.py delete mode 100755 src/devices_controller/devices_controller_abstract_component/device_controller_plc.py delete mode 100755 src/devices_controller/devices_controller_abstract_component/device_controller_telescope.py delete mode 100644 src/devices_controller/devices_controller_concrete/device_controller_AK/.emptyfile delete mode 100755 src/devices_controller/devices_controller_concrete/device_controller_AK/device_controller_plc_ak.py delete mode 100644 src/devices_controller/devices_controller_concrete/device_controller_Gemini/.emptyfile delete mode 100755 src/devices_controller/devices_controller_concrete/device_controller_Gemini/client_telescope_gemini_controller_run.py delete mode 100755 src/devices_controller/devices_controller_concrete/device_controller_Gemini/device_controller_telescope_gemini.py delete mode 100755 src/devices_controller/devices_controller_concrete/device_controller_Gemini/device_simulator_telescope_gemini.py delete mode 100755 src/devices_controller/devices_controller_concrete/device_controller_Gemini/server_telescope_gemini_simulator_run.py delete mode 100755 src/devices_controller/devices_controller_concrete/device_controller_SBIG/device_controller_SBIG.py delete mode 100755 src/devices_controller/devices_controller_concrete/device_controller_SBIG/device_simulator_SBIG.py delete mode 100755 src/devices_controller/devices_controller_concrete/device_simulator_common/server_udp_or_tcp.py delete mode 100755 src/devices_controller/doc/Device_controller_class_diag.pu delete mode 100755 src/devices_controller/doc/Device_controller_class_diag_multiinheritance_OLD.pu delete mode 100755 src/devices_controller/doc/generate_diagrams.sh delete mode 100755 src/devices_controller/logs.py delete mode 100644 src/devices_controller/test/.gitignore delete mode 100755 src/devices_controller/test/test_client_gemini.py diff --git a/.gitignore b/.gitignore index 540a84e..574d785 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ .DS_Store /private /venv*/ +.mypy_cache __pycache__ *.pyc *.py.bak diff --git a/HOWTO_TEST.txt b/HOWTO_TEST.txt index 372430c..564087e 100644 --- a/HOWTO_TEST.txt +++ b/HOWTO_TEST.txt @@ -20,7 +20,9 @@ $ ./pyros.py -ts start agentDeviceTelescopeGemini,agentTelescopeRequester - test with REAL telescope : $ ./pyros.py -t start agentDeviceTelescopeGemini,agentTelescopeRequester - +- (3) Test with agentMultiRequester (AgentMajordome like) sending commands to several device agents at once : the Gemini telescope device agent (or its simulator), and the SBIG device agent : + $ ./pyros.py -t start agentDeviceTelescopeGemini,agentMultiRequester + (C) Interactive testing: $ ./pyros.py shell diff --git a/pyros.py b/pyros.py index 55bf7a8..bdef9ec 100755 --- a/pyros.py +++ b/pyros.py @@ -35,8 +35,10 @@ AGENTS = { "agentB" : "AgentB", "agentM" : "AgentM", #"agentDevice" : "AgentDevice", - "agentDeviceTelescopeGemini" : "agentDeviceTelescopeGemini", + "agentDeviceTelescopeGemini" : "AgentDeviceTelescopeGemini", + "agentDeviceSBIG" : "AgentDeviceSBIG", "agentTelescopeRequester" : "AgentTelescopeRequester", + "agentMultiRequester" : "AgentMultiRequester", "webserver" : "webserver", "monitoring" : "monitoring", "majordome" : "majordome", diff --git a/src/core/pyros_django/agent/Agent.py b/src/core/pyros_django/agent/Agent.py index 363b337..1ecca86 100755 --- a/src/core/pyros_django/agent/Agent.py +++ b/src/core/pyros_django/agent/Agent.py @@ -362,6 +362,8 @@ class Agent: _current_specific_cmd = None _current_specific_thread = None + # List of agents I will send commands to + _my_client_agents = {} _iter_num = None @@ -401,6 +403,16 @@ class Agent: if self.config.get_last_errno() != self.config.NO_ERROR: raise Exception(f"Bad config file name '{config_filename}', error {str(self.config.get_last_errno())}: {str(self.config.get_last_errmsg())}") + #TODO: : à mettre dans la config + self._my_client_agents = { + ''' + 'AgentDeviceTelescope1': 'AgentDeviceTelescopeGemini', + 'AgentDeviceFilterSelector1': 'AgentDeviceSBIG', + 'AgentDeviceShutter1': 'AgentDeviceSBIG', + 'AgentDeviceSensor1': 'AgentDeviceSBIG', + ''' + } + ### self._agent_logs = AgentLogs.objects.create(name=self.name, message="Step __init__") self.printdb("Step __init__") @@ -460,6 +472,17 @@ class Agent: else: time.sleep(nbsec) + def _get_real_agent_name_for_alias(self, agent_alias_name:str): + if not self._my_client_agents: return agent_alias_name + return self._my_client_agents[agent_alias_name] + + def build_cmd(self, recipient_agent_alias_name, cmd_name): + return Command( + sender=self.name, + recipient=self._get_real_agent_name_for_alias(recipient_agent_alias_name), + name=cmd_name + ) + def run(self, nb_iter:int=None, FOR_REAL:bool=True): """ FOR_REAL: set to False if you don't want Agent to send commands to devices but just print messages without really doing anything @@ -568,7 +591,7 @@ class Agent: self.print("-"*20, "MAIN LOOP ITERATION (END)", "-"*20) #self.do_log(LOG_DEBUG, "Ending main loop iteration") - # (simulator only) Exit if max duration is reached + # (Test only) Exit if max duration is reached if self.TEST_MAX_DURATION_SEC and (time.time()-self.start_time > self.TEST_MAX_DURATION_SEC): self.print("Exit because of max duration set to ", self.TEST_MAX_DURATION_SEC, "s") self.kill_running_specific_cmd_if_exists(self.name) @@ -831,7 +854,7 @@ class Agent: #ex: send_command(“AgentX”,”GENERIC”,”EVAL”,“3+4”) ex: send_command(“AgentX”,"EVAL”,“3+4”) """ - return Command.send_command(self.name, to_agent, cmd_name, cmd_args) + return Command.send_command(self.name, self._get_real_agent_name_for_alias(to_agent), cmd_name, cmd_args) def get_next_valid_and_not_running_command(self)->Command: """ @@ -1144,11 +1167,13 @@ class Agent: self.WITH_SIMULATOR=mode def simulator_get_next_command_to_send(self)->Command: - cmd_name = next(self.TEST_COMMANDS, None) + cmd = next(self.TEST_COMMANDS, None) #return cmd_name - if cmd_name is None: return None - recipient_agent = self.name if self.TEST_COMMANDS_DEST=="myself" else self.TEST_COMMANDS_DEST - return Command(sender=self.name, recipient=recipient_agent, name=cmd_name) + if cmd is None: return None + agent_recipient,cmd_name = cmd.split(':') + ##recipient_agent = self.name if self.TEST_COMMANDS_DEST=="myself" else self.TEST_COMMANDS_DEST + #return Command(sender=self.name, recipient=recipient_agent, name=cmd_name) + return self.build_cmd(agent_recipient, cmd_name) """ def simulator_send_next_command(self): diff --git a/src/core/pyros_django/agent/AgentDevice.py b/src/core/pyros_django/agent/AgentDevice.py index 45a22d3..08a96e1 100755 --- a/src/core/pyros_django/agent/AgentDevice.py +++ b/src/core/pyros_django/agent/AgentDevice.py @@ -10,7 +10,9 @@ import time sys.path.append("..") from agent.Agent import Agent, build_agent from common.models import AgentDeviceStatus, get_or_create_unique_row_from_model + sys.path.append("../../..") +from device_controller.abstract_component.base import DeviceControllerAbstract ##log = L.setupLogger("AgentXTaskLogger", "AgentX") @@ -81,7 +83,7 @@ class AgentDevice(Agent): # @override #def __init__(self, name:str=None, config_filename=None, RUN_IN_THREAD=True, device_controller, host, port): #def __init__(self, name:str, config_filename, RUN_IN_THREAD, device_controller, host, port, device_simulator): - def __init__(self, config_filename, RUN_IN_THREAD, device_controller, host, port, device_simulator): + def __init__(self, config_filename, RUN_IN_THREAD, device_controller:DeviceControllerAbstract, host, port, device_simulator): ##if name is None: name = self.__class__.__name__ #super().__init__(name, config_filename, RUN_IN_THREAD) super().__init__(config_filename, RUN_IN_THREAD) @@ -234,7 +236,7 @@ class AgentDevice(Agent): # Define your own command step(s) here def cmd_step(self, step:int): - cmd = self._current_specific_cmd + cmd = self._current_specific_cmd res = self._device_ctrl.execute_cmd(cmd.name) cmd.set_result(str(res)) print("result is", str(res)) diff --git a/src/core/pyros_django/agent/AgentDeviceSBIG.py b/src/core/pyros_django/agent/AgentDeviceSBIG.py index 259c4b8..b35fe8d 100755 --- a/src/core/pyros_django/agent/AgentDeviceSBIG.py +++ b/src/core/pyros_django/agent/AgentDeviceSBIG.py @@ -11,11 +11,11 @@ sys.path.append("..") from agent.AgentDevice import AgentDevice, build_agent from common.models import AgentDeviceTelescopeStatus, get_or_create_unique_row_from_model sys.path.append("../../..") -##from devices_controller.devices_controller_concrete.device_simulator_common.server_udp_or_tcp import get_SocketServer_UDP_TCP +##from device_controller.concrete_component.device_simulator_common.server_udp_or_tcp import get_SocketServer_UDP_TCP -from devices_controller.devices_controller_concrete.device_controller_Gemini.telescope_gemini_simulator import TelescopeGEMINISimulator +from device_controller.concrete_component.sbig.device_simulator_SBIG import DeviceSimulatorSBIG -from devices_controller.devices_controller_concrete.device_controller_Gemini.telescope_controller_gemini import TelescopeControllerGEMINI +from device_controller.concrete_component.sbig.SBIG import DeviceControllerSBIG ##log = L.setupLogger("AgentXTaskLogger", "AgentX") @@ -66,9 +66,9 @@ class AgentDeviceSBIG(AgentDevice): super().__init__( config_filename, RUN_IN_THREAD, - device_controller=TelescopeControllerGEMINI, + device_controller=DeviceControllerSBIG, host=self.HOST, port=self.PORT, - device_simulator=TelescopeGEMINISimulator) + device_simulator=DeviceSimulatorSBIG) # Initialize the device table status # If table is empty, create a default 1st row @@ -86,7 +86,7 @@ class AgentDeviceSBIG(AgentDevice): # Port local AK 8085 = redirigé sur l’IP du tele 192.168.0.12 sur port 11110 ##HOST, PORT = "82.64.28.71", 11110 #HOST, PORT = "localhost", 11110 - ##self.tele_ctrl = TelescopeControllerGEMINI(HOST, PORT, True) + ##self.tele_ctrl = TelescopeControllerGemini(HOST, PORT, True) ##self._log.print(f"init done for {name}") @@ -101,7 +101,7 @@ class AgentDeviceSBIG(AgentDevice): #HOST, PORT = "localhost", 11110 #with get_SocketServer_UDP_TCP(HOST, PORT, "UDP") as myserver: #with get_SocketServer_UDP_TCP(self.HOST, self.PORT, "UDP") as myserver: myserver.serve_forever() - TelescopeGEMINISimulator.serve_forever(self.PORT) + TelescopeGeminiSimulator.serve_forever(self.PORT) ''' myserver = get_SocketServer_UDP_TCP(self.HOST, self.PORT, "UDP") myserver.serve_forever() @@ -171,7 +171,7 @@ if __name__ == "__main__": # with process #RUN_IN_THREAD=False - agent = build_agent(AgentDeviceTelescopeGemini, RUN_IN_THREAD=RUN_IN_THREAD) + agent = build_agent(AgentDeviceSBIG, RUN_IN_THREAD=RUN_IN_THREAD) ''' TEST_MODE, WITH_SIM, configfile = extract_parameters() #agent = AgentX() diff --git a/src/core/pyros_django/agent/AgentDeviceTelescopeGemini.py b/src/core/pyros_django/agent/AgentDeviceTelescopeGemini.py index c2f1405..6dedcc4 100755 --- a/src/core/pyros_django/agent/AgentDeviceTelescopeGemini.py +++ b/src/core/pyros_django/agent/AgentDeviceTelescopeGemini.py @@ -11,11 +11,11 @@ sys.path.append("..") from agent.AgentDevice import AgentDevice, build_agent from common.models import AgentDeviceTelescopeStatus, get_or_create_unique_row_from_model sys.path.append("../../..") -##from devices_controller.devices_controller_concrete.device_simulator_common.server_udp_or_tcp import get_SocketServer_UDP_TCP +##from device_controller.concrete_component.device_simulator_common.server_udp_or_tcp import get_SocketServer_UDP_TCP -from devices_controller.devices_controller_concrete.device_controller_Gemini.device_simulator_telescope_gemini import DeviceSimulatorTelescopeGemini +from device_controller.concrete_component.gemini.gemini_telescope_simulator import DeviceSimulatorTelescopeGemini -from devices_controller.devices_controller_concrete.device_controller_Gemini.device_controller_telescope_gemini import DeviceControllerTelescopeGemini +from device_controller.concrete_component.gemini.gemini_telescope_controller import DeviceControllerTelescopeGemini ##log = L.setupLogger("AgentXTaskLogger", "AgentX") diff --git a/src/core/pyros_django/agent/AgentMultiRequester.py b/src/core/pyros_django/agent/AgentMultiRequester.py new file mode 100755 index 0000000..0f664e6 --- /dev/null +++ b/src/core/pyros_django/agent/AgentMultiRequester.py @@ -0,0 +1,268 @@ +#!/usr/bin/env python3 + + +import sys +##import utils.Logger as L + +##from .Agent import Agent +sys.path.append("..") +from agent.Agent import Agent, build_agent + + +##log = L.setupLogger("AgentXTaskLogger", "AgentX") + + + +class AgentMultiRequester(Agent): + + # FOR TEST ONLY + # Run this agent in simulator mode + TEST_MODE = True + # Run the assertion tests at the end + TEST_WITH_FINAL_TEST = True + #TEST_MAX_DURATION_SEC = None + TEST_MAX_DURATION_SEC = 75 + # Who should I send commands to ? + #TEST_COMMANDS_DEST = "myself" + #TEST_COMMANDS_DEST = "AgentDeviceTelescopeGemini" + # Scenario to be executed + TEST_COMMANDS_LIST = [ + # Ask receiver to delete all its previous commands + 'AgentDeviceTelescope1:flush_commands', + 'AgentDeviceFilterSelector1:flush_commands', + 'AgentDeviceShutter1:flush_commands', + 'AgentDeviceSensor1:flush_commands', + + 'AgentDeviceTelescope1:go_active', + + # Because of this command, the receiver agent : + # - will no more send any new command + # - will only execute "generic" commands (and not the "specific" ones) + 'AgentDeviceTelescope1:go_idle', + + # Not executed (skipped) because receiver agent is now "idle" + #"specific0", + + # Because of this command, the receiver agent + # will now be able to send new commands + 'AgentDeviceTelescope1:go_active', + + # Executed because recipient agent is now "active" + #"specific1", + 'AgentDeviceTelescope1:get radec', + # should abort previous command (specific1) + #"abort", + + # Executed completely because no abort + #"specific2", + + # fully executed, result is 7 + #"eval 4+3", + + 'AgentDeviceTelescope1:go_idle', + + # Now stop all my device agent clients: + 'AgentDeviceTelescope1:exit', + 'AgentDeviceFilterSelector1:exit', + 'AgentDeviceShutter1:exit', + 'AgentDeviceSensor1:exit', + + ] + + + + """ + ================================================================= + FUNCTIONS RUN INSIDE MAIN THREAD + ================================================================= + """ + + # @override + #def __init__(self, name:str=None, config_filename=None, RUN_IN_THREAD=True): + def __init__(self, config_filename=None, RUN_IN_THREAD=True): + ##if name is None: name = self.__class__.__name__ + #super().__init__(name, config_filename, RUN_IN_THREAD) + super().__init__(config_filename, RUN_IN_THREAD) + + #TODO: : à mettre dans la config + self._my_client_agents = { + 'AgentDeviceTelescope1': 'AgentDeviceTelescopeGemini', + 'AgentDeviceFilterSelector1': 'AgentDeviceSBIG', + 'AgentDeviceShutter1': 'AgentDeviceSBIG', + 'AgentDeviceSensor1': 'AgentDeviceSBIG', + } + + #self._log.print(f"init done for {self.name}") + self._log.print(f"init done") + + # @override + def init(self): + super().init() + # --- Set the mode according the startmode value + ##agent_alias = self.__class__.__name__ + ##self.set_mode_from_config(agent_alias) + + ''' + # @override + def load_config(self): + super().load_config() + ''' + + ''' + # @override + def update_survey(self): + super().update_survey() + ''' + + ''' + # @override + def get_next_command(self): + return super().get_next_command() + ''' + + # @override + def do_log(self): + super().do_log() + + + + """ + ================================================================= + FUNCTIONS RUN INSIDE A SUB-THREAD (OR A PROCESS) (thread_*()) + ================================================================= + """ + + # Define your own command step(s) here + def cmd_step1(self, step:int): + cmd = self._current_specific_cmd + cmd.result = f"in step #{step}/{self._thread_total_steps_number}" + cmd.save() + """ + if self.RUN_IN_THREAD: + print("(save from thread)") + cmd.save() + else: + #@transaction.atomic + print("(save from process)") + with transaction.atomic(): + cmd.save() + #Command.objects.select_for_update() + """ + + def cmd_step2(self, step:int): + self.cmd_step1(step) + def cmd_step3(self, step:int): + self.cmd_step1(step) + def cmd_step4(self, step:int): + self.cmd_step1(step) + + """ + # @override + def thread_exec_specific_cmd_step(self, step:int, sleep_time:float=1.0): + self.thread_stop_if_asked() + cmd = self._current_specific_cmd + print(f">>>>> Thread (cmd {cmd.name}): step #{step}/5") + self.sleep(sleep_time) + """ + + ''' + # @override + def exec_specific_cmd_start(self, cmd:Command, from_thread=True): + super().exec_specific_cmd_start(cmd, from_thread) + ''' + + + # @override + def thread_exec_specific_cmd_main(self): + # This is optional + self.thread_set_total_steps_number(5) + + # HERE, write your own scenario + + # scenario OK + self.thread_exec_specific_cmd_step(1, self.cmd_step1, 1) + self.thread_exec_specific_cmd_step(2, self.cmd_step2, 3) + self.thread_exec_specific_cmd_step(3, self.cmd_step1, 5) + self.thread_exec_specific_cmd_step(4, self.cmd_step3, 10) + self.thread_exec_specific_cmd_step(5, self.cmd_step1, 4) + # ... as many as you need + + """ autre scenario + self.thread_exec_specific_cmd_step(1, self.cmd_step1, 1) + self.thread_exec_specific_cmd_step(2, self.cmd_step2, 2) + self.thread_exec_specific_cmd_step(3, self.cmd_step1, 2) + self.thread_exec_specific_cmd_step(4, self.cmd_step3, 2) + self.thread_exec_specific_cmd_step(5, self.cmd_step1, 3) + """ + + ''' + # @override + def exec_specific_cmd_end(self, cmd:Command, from_thread=True): + super().exec_specific_cmd_end(cmd, from_thread) + ''' + + # @override + def simulator_test_results_main(self, commands): + nb_asserted = 0 + for cmd in commands: + if cmd.name == "flush_commands": + assert cmd.is_executed() + nb_asserted+=1 + # 2 times + if cmd.name == "go_active": + assert cmd.is_executed() + nb_asserted+=1 + # 2 times + if cmd.name == "go_idle": + assert cmd.is_executed() + nb_asserted+=1 + if cmd.name == "get radec": + assert cmd.is_executed() + #assert cmd.result == "06:10:38,+89:41:02" + nb_asserted+=1 + """ + if cmd.name == "specific0": + assert cmd.is_skipped() + assert cmd.result == "in step #5/5" + nb_asserted+=1 + if cmd.name == "specific1": + assert cmd.is_killed() + nb_asserted+=1 + if cmd.name == "specific2": + assert cmd.is_executed() + assert cmd.result == "in step #5/5" + nb_asserted+=1 + if cmd.name == "eval 4+3": + assert cmd.is_executed() + assert cmd.get_result() == "7" + nb_asserted+=1 + if cmd.name in ("abort"): + assert cmd.is_executed() + nb_asserted+=1 + """ + if cmd.name in ("exit"): + assert cmd.is_executed() + nb_asserted+=1 + return nb_asserted + + +""" +================================================================= + MAIN FUNCTION +================================================================= +""" +if __name__ == "__main__": + # with thread + RUN_IN_THREAD=True + # with process + #RUN_IN_THREAD=False + + agent = build_agent(AgentMultiRequester, RUN_IN_THREAD=RUN_IN_THREAD) + ''' + TEST_MODE, WITH_SIM, configfile = extract_parameters() + #agent = AgentX() + agent = AgentTelescopeRequester("AgentTelescopeRequester", configfile, RUN_IN_THREAD) + agent.setSimulatorMode(TEST_MODE) + print(agent) + ''' + agent.run() diff --git a/src/core/pyros_django/majordome/doc/AgentMajordome_object_diag.pu b/src/core/pyros_django/majordome/doc/AgentMajordome_object_diag.pu index d1ecf42..059fa5b 100755 --- a/src/core/pyros_django/majordome/doc/AgentMajordome_object_diag.pu +++ b/src/core/pyros_django/majordome/doc/AgentMajordome_object_diag.pu @@ -52,11 +52,11 @@ object AgentDeviceShutter2 /' Use l,r,u,d for left, right, up, or down row alignement '/ -AgentMajordome -d-> AgentDeviceFilterSelector1 : sends command to -AgentMajordome -d-> AgentDeviceShutter1 : sends command to -AgentMajordome -d-> AgentDeviceSensor1 : sends command to -AgentMajordome -d-> AgentDeviceTelescope1 : sends command to -AgentMajordome -d-> AgentDeviceShutter2 : sends command to +AgentMajordome -d-> AgentDeviceFilterSelector1 : sends cmd to +AgentMajordome -d-> AgentDeviceShutter1 : sends cmd to +AgentMajordome -d-> AgentDeviceSensor1 : sends cmd to +AgentMajordome -d-> AgentDeviceTelescope1 : sends cmd to +AgentMajordome -d-> AgentDeviceShutter2 : sends cmd to object AgentDeviceSBIG_CXZ347 <> diff --git a/src/device_controller/.gitignore b/src/device_controller/.gitignore new file mode 100644 index 0000000..c8a02eb --- /dev/null +++ b/src/device_controller/.gitignore @@ -0,0 +1,2 @@ +/client.log +/**/client.log diff --git a/src/device_controller/README.md b/src/device_controller/README.md new file mode 100644 index 0000000..5e4dc62 --- /dev/null +++ b/src/device_controller/README.md @@ -0,0 +1,337 @@ + +************************ +## DEVICES & CHANNELS +************************ +VERSION: 0.30.0 + +Date: 19/09/2019 + +By: epallier@irap.omp.eu + + + + + + + +******************************************************************************************** +# 1) TEST + +Pour lancer les TESTS: + + $ cd test/ + $ ./test_client_gemini.py + (test de connexion locale avec un "simulateur" de Gemini) + +Ca doit se terminer par quelque chose comme: +Ran 2 tests in 0.013s +OK + +Là, il faut arrêter avec CTRL-C (car je ne sais pas encore comment arrêter le simulateur de Telescope (serveur) autrement !) + + + +******************************************************************************************** +# 2) RUN + +Pour lancer le client sur le telescope Gemini de Alain Klotz: + + $ cd devices_controller_concrete/device_controller_Gemini/ + $ ./client_telescope_gemini_controller_run.py + (Windows: python3 client_telescope_controller_gemini_run.py) + +(NB: un log "client.log" est créé et alimenté au fur et à mesure) +(Pour avoir moins de détail, mettre la variable DEBUG à False dans le fichier telescope_controller_gemini_run.py) +(press ENTER to quit) + +Examples of requests: + + REQUEST TO SERVER (ENTER to quit): >>> :GR# + NATIVE Command to send is ':GR#' + Sent: b'00090000:GR#\x00' + Sent 13 bytes + RECEIVED (ALL BYTES): b'0009000015:01:48#\x00' + RECEIVED (useful data): 15:01:48 + + REQUEST TO SERVER (ENTER to quit): >>> get ra + GENERIC Command to send is get_ra + NATIVE Command to send is ':GR#' + Sent: b'00100000:GR#\x00' + Sent 13 bytes + RECEIVED (ALL BYTES): b'0010000015:01:48#\x00' + RECEIVED (useful data): 15:01:48 + result is 15:01:48 + + + +******************************************************************************************** +# 3) DEV + +Pour lancer le même client seulement sur le "simulateur" de telescope (localhost, port 11110), ajouter "local" à la fin: + + $ cd devices_controller_concrete/device_controller_Gemini/ + $ ./client_telescope_gemini_controller_run.py local + +Dans un autre terminal, lancer le simulateur: + + $ ./server_telescope_gemini_simulator_run.py + (CTRL-C to stop it) + + +******************************************************************************************** +# 4) DONE + + 5/10/18: + + - greatly improved (plant)UML class diagram(s) + + + 4/10/18: + + - BIG REORGANIZATION OF CODE + => new class DeviceControllerAbstract which has a component ClientChannel (of type "socket", "serial", or "usb") + - tests ok + - cleanup + + + 2/10/18: + + - included "doctests" in tests (try it by running "cd test/ ; ./test_client_gemini.py") + - started plc client + + - improved telescope simulator server answers : do_goto(), do_move(), do_init() + + - improved telescope simulator server answers + - tests pass ok + + - decorator @generic_cmd + + - abstract commands dictionary is now the default one and is ovewritable with native dictionary + - native telescope class is now less than 300 lines (majority of code is in the abstract class so that it is easy to make a new concrete telescope class): + => ne contient quasiment QUE le dictionnaire des commandes natives + - cleanup + + + 1/10/18: + + - interpreteur de cde generique et native : set ra 20:00:00 ou :GR# + - execute_cmd(): + - execute_generic_cmd() + - execute_native_cmd() + - GenericResult() + + + 28/9/18: + + - move do_init() to abstract class + - generic return code for each command + + - clean code + - move do_goto() and move_dir() to abstract class + + - doctests + + + 27/9/18: + + - GOTO + - finalize do_init() + + - set date&time, set lat&long + - README file enriched + - help => liste cdes possibles + - (X) set DATE & TIME (p109) + - Ce que fait AK avec TCL ou C: sprintf(ligne,":SC%02d/%02d/%02d#:SL%02d:%02d:%02.0f#:SG+00#",m,d,y-2000,h,min,s); + 1) :SG+00# + => TU + 2) SC%02d/%02d/%02d#: + mois, jour, YY + 3) SL%02d:%02d:%02.0f#: + hh:mn:ss (heure locale) + m,d,y-2000,h,min,s + Faire les 3 commandes ensemble en 1 seule ligne, chacune séparée par #, le tout terminé par « 00 » + + - generic commands available from client console + - generic commands implemented + + - procedure initialize() + + - ACK (cde 6) + + - 3 types of commands + - \x00 at end + + - classe SocketClientAbstract de base + + + +******************************************************************************************** +5) WORK CURRENTLY IN PROGRESS... + + - main updated + - tests updated + - comment functions args + + + +******************************************************************************************** +6) TODO LIST + + - INTEGRATION INTO PYROS + + - LOG: avoir le nom du module qui logue et non pas only logs.py + + - interpreteur de cde generique (avec celme pour les values) : set ra 20:00:00 + + - Tele MEADE ETX (altaz) : cf doc pdf (from page 1) + - peut être en 3 modes : land, altaz, polar (equatorial) + - :AP# => passer en mode polar + - :AA# => passer en mode AltAz + - cette config doit être fait dans do_init() + - doc page 1 : x=implémenté, p=partial, - = pas implémenté + + - Position class + + - POSITION (p103): + - (100) MOVE() (p104-105) + => en altaz ou radec + => préférer "haut, bas, gauche, droite" plutôt que North, South, west, east (surtout sur altaz ça n'a aucun sens) + => sur equatorial, h,b = DEC et g,d = RA + - set_speed() + - start(direction) avec direction = h,b,g,d => move infini + - stop() + - pulse(direction, nbsec) = start() puis sleep(nbsec) puis stop() + - éventuellement start_diagonal qui ferait start(up) et start(right) en alternance (pas prioritaire) + - MOVE(direction, rate, duration): + - move dans une direction (N,S,E,O) à l'infini + - ':Q#' pour arrêter (p 108) + - duration: infinie si pas donnée + - rate (p108): 4 vitesses différentes (prendre la plus lente par défaut), entre 0 et 1: + - [0-0.25] : RC => Rate Center. Subsequent Move commands will move at Centering Speed. + - [0.25-0.50] : RG => Rate Guide. Subsequent Move commands will move at Guiding Speed. + - [0.50-0.75] : RM => Rate Move. Subsequent Move commands will move at Centering Speed. + - [0.75-1.0] : RS => Rate Slew. Subsequent Move commands will move at Slewing Speed. + + - (100) GOTO: + - bloquant ou non + - 1) goto_setmode(radec | hadec | altaz) + - 2) goto() + - Attention, gemini ne comprend que radec, les tarot ne comprennent que hadec !!! : + => goto_setmode(radec) ne fait rien sur un gemini, mais convertit sur un tarot + + - (100) vitesse de pointage (slew speed) : n'existe pas sur tous les tele + + - drift = tracking speed : une fois que tele a pointé, comment il part... + - toujours en hadec + - tracking_speed = 0 => tele est fixe + - t.tracking_speed(ha ou ra, dec) => ha est en nb degrés/sec + => t.tracking_speed(0,0) + => t.tracking_speed("diurnal" ou "sideral",0) // sideral c'est mieux + - jour solaire : 86400s/jour + - jour sideral (diurnal) = durée d'une journée pour une étoile = 86164s/jour (moins que jour solaire, à cause du fait que en 24h, la Terre a avancé sur son orbite circulaire) + - nb degrés/sec = 360/86400 pour jour solaire, et 360/86164 pour jour sideral + + + - (200) timeout : 1 pour send, 1 pour receive + + - (200) celme.angles => infos() à généraliser + + - (200) type erreurs + - 1000 : communication (network, cable débranché) = channel + - 2000 : syntax + - 3000 : out of limit (pointage hors limite, ex: sur le sol) + - 4000 : controller + - 5000 : file (disk...) + + - (200) generic functions return tuple with error code : + res = get_ra() + => res.res is the result + => res.error is the error object : err.error_code, err.generic_msg, err.native_msg + => ou plutot: + res,error = get_ra() + res,_ = get_ra() # pour ignorer le code erreur + res is the result + error is the error object + + - (200) PLC abstract client + + - (100) input commands format : + - get ra + - set ra "2h3m27s" => converted by celme.Angle(ra).deg() + + - (100) _cmds = list of functions (???) + + - remplacer utf-8 par unicode ou iso... + + - cdes 05 (p100): + - cde 05 => return long list of parameters (= GROS GET) + - ENQ = 05 + + - Table d'attributs pour chaque telescope (config) + + - _connect() ou connect() ? + + - Implémenter les commandes NATIVES (non LX-200) : + - < ou >, termine par ':' + checksum + # + + + + + + + + + + + + +******************************************************************************************** +7) INFORMATIONS + + + GMT = TU décalé de 12h, mais maintenant c'est pareil + TU = UTC civil, voir aussi UT1, UT2 + + COMMANDES LX-200: + - SA et SZ pour envoyer coord en alt-az + + RA-DEC is converted into "pas codeurs" + 1 pas codeur (mvmt interne du tele) = environ 1 sec arc sur le ciel + (en gros, RA = petit axe du tele, DEC = grand axe du tele) + + Parking = vers le nord (cf photo) = en position CWD (Counter Weight Down) (contre-poids en bas ?) + + Par défaut, le tele fonctionne en RADEC J2000 + + http://82.64.28.71:8083/L5V1serial.html + + + Ce qui marche: + + - REQUEST TO SERVER [ex: ':GD#' (Get Dec), ':GR#' (Get RA)']: 01000000:GD# + Received (all data): 01000000+90:00:00# + + - REQUEST TO SERVER [ex: ':GD#' (Get Dec), ':GR#' (Get RA)']: 01000000:GR# + Received (all data): 0100000015:01:48# + - REQUEST TO SERVER [ex: ':GD#' (Get Dec), ':GR#' (Get RA)']: 01000000:GR# + Received (all data): 0100000016:04:17# + + - Get Software Level l(one digit) and Version vv(two digits) + REQUEST TO SERVER [ex: ':GD#' (Get Dec), ':GR#' (Get RA)']: 01000000:GV# + Received (all data): 01000000510# + + - REQUEST TO SERVER [ex: ':GD#' (Get Dec), ':GR#' (Get RA)']: 01000000:GG# + Received (all data): 01000000+00# + + - Get Maximum Velocity of both axes (N = No mvmt) + REQUEST TO SERVER [ex: ':GD#' (Get Dec), ':GR#' (Get RA)']: 01000000:Gv# + Received (all data): 01000000N + + + + + + + + + diff --git a/src/device_controller/__init__.py b/src/device_controller/__init__.py new file mode 100644 index 0000000..efcff89 --- /dev/null +++ b/src/device_controller/__init__.py @@ -0,0 +1 @@ +#default_app_config = "agent.apps.AgentConfig" diff --git a/src/device_controller/abstract_component/base.py b/src/device_controller/abstract_component/base.py new file mode 100755 index 0000000..a2eba2f --- /dev/null +++ b/src/device_controller/abstract_component/base.py @@ -0,0 +1,564 @@ +#!/usr/bin/env python3 + +"""Socket Client Telescope (abstract) implementation + +To be used as a base class (interface) for any concrete socket client telescope class +""" + + + + +# Standard library imports +#from enum import Enum +import functools +import logging +import socket +import sys +import time + +# Third party imports + +# from sockets_tele/ +sys.path.append("..") +# from src_socket/client/ +sys.path.append("../../..") +sys.path.append("../../../..") +#import src.core.pyros_django.utils.celme as celme +import src.core.celme as celme +from device_controller.logs import * + + +# Local application imports +#sys.path.append('../..') +#from src.client.socket_client_abstract import UnknownCommandException, SocketClientAbstract +##from src_socket.client.socket_client_abstract import * +##from src_device.client.client_channel import * +from device_controller.channels.client_channel_socket import ClientChannelSocket +from device_controller.channels.client_channel_serial import ClientChannelSerial +from device_controller.channels.client_channel_usb import ClientChannelUSB + + + + +# Execute also "set" and "do" commands +GET_ONLY=False +# Execute only "get" commands +#GET_ONLY=True + +# Default timeouts +TIMEOUT_SEND = 10 +TIMEOUT_RECEIVE = 10 + + +''' +class c(Enum): + + # GET, SET + DEC = 'DEC' + RA = 'RA' + RA_DEC = 'RA_DEC' + + # DO + PARK = 'PARK' + WARM_START = 'WARM_START' +''' + +# DECORATOR +def generic_cmd(func): + #def wrapper_generic_cmd(*args, **kwargs): + @functools.wraps(func) + def wrapper_generic_cmd(self, values_to_set=None): + #print("func name is", func.__name__) + return self.execute_generic_cmd(func.__name__, values_to_set) + return wrapper_generic_cmd + + + +class GenericResult: + ''' Usage: + res = execute(command) + print("result is", res) + if res.ko: raise UnexpectedReturnCode() + if res.ok: + ... + ''' + # By default, bad result + ok = True + ko = False + + def __init__(self, native_result:str, ok=True): + self.txt = native_result + self.ok = ok + self.ko = not ok + def __str__(self): + return self.txt + ''' + def __repr__(self): + return self.txt + def __get__(self, instance, owner): + return self.b + def __set__(self, instance, value): + self.b = value + ''' + + + +class UnexpectedCommandReturnCode(Exception): + pass +class TimeoutException(Exception): + pass +class UnknownCommandException(Exception): + pass + ''' + def __init__(self,*args,**kwargs): + super().__init__(self,*args,**kwargs) + ''' + + + +#TODO: remove ClientChannelAbstract, and set instead a ClientChannel +#class DeviceControllerAbstract(SocketClientAbstract): +##class DeviceControllerAbstract(ClientChannel): +class DeviceControllerAbstract(): + + # ClientChannel used by the device controller (to be set during __init__ via set_client_channel()) + my_channel = None + + # @abstract (to be overriden) + _cmd_device_concrete = {} + _cmd_device_abstract = {} + _cmd = { + # GET-SET commands: + + 'get_timezone': [], + 'set_timezone': [], + + 'get_date': [], + 'set_date': [], + + 'get_time': [], + 'set_time': [], + + # DO commands: + 'do_init': ['do_init'], + 'do_park': [], + } + + + ##def __init__(self, device_host:str="localhost", device_port:int=11110, PROTOCOL:str="TCP", buffer_size=1024, DEBUG=False): + def __init__(self, device_host:str="localhost", device_port:int=11110, PROTOCOL:str="SOCKET-TCP", buffer_size=1024, DEBUG=False): + ''' + :param device_host: server IP or hostname + :param device_port: server port + :param PROTOCOL: "SOCKET-TCP", "SOCKET-UDP", "SERIAL", or "USB" + ''' + ##super().__init__(device_host, device_port, PROTOCOL, buffer_size, DEBUG) + set_logger(DEBUG) + log_d("Logger configured") + + if PROTOCOL.startswith("SOCKET"): self.my_channel:ClientChannel = ClientChannelSocket(device_host, device_port, PROTOCOL, buffer_size, DEBUG) + elif PROTOCOL == "SERIAL": self.my_channel:ClientChannel = ClientChannelSerial(device_host, device_port, buffer_size, DEBUG) + elif PROTOCOL == "USB": self.my_channel:ClientChannel = ClientChannelUSB(device_host, device_port, buffer_size, DEBUG) + else: raise Exception("Unknown Channel", PROTOCOL) + + # overwrite abstract _cmd dictionary with subclass native _cmd_native dictionary: + #self._cmd = {**self._cmd, **self._cmd_native} + self._cmd = {**self._cmd, **self._cmd_device_abstract, **self._cmd_device_concrete} + + + # So that we can use this with the "with" statement (context manager) + def __enter__(self): + return self + def __exit__(self, type, value, traceback): + self.my_channel.__exit__(type, value, traceback) + + ''' + def set_logger(self, DEBUG): + self.my_channel.set_logger(DEBUG) + ''' + + def _connect_to_device(self): + self.my_channel._connect_to_server() + + def get_celme_longitude(self, longitude): + return celme.Angle(longitude).sexagesimal("d:+0180.0") + def get_celme_latitude(self, latitude): + return celme.Angle(latitude).sexagesimal("d:+090.0") + + #@override ClientChannel send_data + def send_data(self, data:str): + data_encapsulated = self.format_data_to_send(data) + self.my_channel.send_data(data_encapsulated) + ''' + The chosen way to send data is this: + # - :GD# + b'00030000:GD#\x00' + + Another way to send data (which also works), is it better ? + # - :GD# + ###tsock.mysock.sendto(b'\x00\x00\x00\x01\x00\x00\x00\x00\x3A\x47\x44\x23\x00', (HOST, PORT)) + # - :GR# + ###tsock.mysock.sendto(b'\x00\x00\x00\x01\x00\x00\x00\x00\x3A\x47\x52\x23\x00', (HOST, PORT)) + # - ACK 06 OK !!! : + tsock.mysock.sendto(b'\x00\x00\x00\x01\x00\x00\x00\x00\x00\x06\x00\x00', (HOST, PORT)) + + Which one is the best method ? + ''' + #log_d("NATIVE Command to send is "+repr(data)) + ##encapsulated_data = self.encapsulate_data_to_send(data) + #print("before _send", encapsulated_data) + #print("before _send", repr(encapsulated_data)) + ##self._send_data(encapsulated_data) + ##self.my_channel.send_data(encapsulated_data) + #log_i(f'Sent: {encapsulated_data}') + ''' + def _send_data(self, data): + self.my_channel._send_data(data) + ''' + + #@override ClientChannel receive_data + def receive_data(self)->str: + ##data_received_bytes = self._receive_data() + data_received = self.my_channel.receive_data() + #log_d("Received (all data): {}".format(data_received)) + #log_d("data in bytes: "+str(bytes(data_received, "utf-8"))) + data = self.unformat_received_data(data_received) + log_i("RECEIVED (useful data): {}".format(data)) + return data + ''' + def _receive_data(self): + return self.my_channel._receive_data() + ''' + + # Encapsulate useful data to be ready for sending + # By default, do nothing + #@abstract + def format_data_to_send(self, data:str): + return self.encapsulate_data_to_send(data) + #@deprecated + def encapsulate_data_to_send(self, data:str): + return data + + # Extract useful data from received raw data + # By default, do nothing + #@abstract + def unformat_received_data(self, data:str): + return self.uncap_received_data(data) + #@deprecated + def uncap_received_data(self, data:str): + #return data_received.decode() + return data + + ''' + def encapsulate_data_to_send(self, command:str): + return self.my_channel.encapsulate_data_to_send(command) + def uncap_received_data(self, data_received:str): + return self.my_channel.uncap_received_data(data_received) + ''' + + + def get_utc_date(self): + return celme.Date("now").iso(0) + #return celme.Date("now").ymdhms() + + + def close(self): + self.my_channel.close() + + + def is_generic_cmd(self, raw_input_cmd:str) -> bool: + # Using Google documentation format (https://www.sphinx-doc.org/en/master/usage/extensions/example_google.html#example-google) + """ Is this a generic command ? + + Args: + raw_input_cmd: a command in raw string format (like 'get ra' or 'set ra 20:00:00' or 'set radec 20:00:00 90:00:00" or 'do park' or 'do_park'...) + + Returns: + either False or (cmd, [values]) with cmd like "get_ra" (using underscore '_' instead of space ' '). + + """ + #return cmd.startswith('get_') or cmd.startswith('set_') or cmd.startswith('do_') + #cmds = ['get ', 'get_', 'set ', 'set_', 'do ', 'do_'] + ''' + seps = (" ", "_") + #cmds = list(x+y for x in cmd for y in sep) + for cmd in cmds: + for sep in seps: + generic_cmd = cmd+sep + if raw_input_cmd.startswith(generic_cmd): + # Is there value(s) passed ? + if len(raw_input_cmd) > len(generic_cmd): + values = raw_input_cmd[len(generic_cmd):] + values = values.split(' ') + # return cmd like "get_ra", [and values] + return generic_cmd.replace(' ','_'), values + return False, False + ''' + values_to_set = None + cmds = ("get","set","do") + raw_input_cmd = raw_input_cmd.strip() + cmd_splitted = raw_input_cmd.split(' ') + if len(cmd_splitted) == 1: return False,False + # ex: "set_radec" + generic_cmd = cmd_splitted[0] + '_' + cmd_splitted[1] + # Check this generic command exists + #if (generic_cmd not in self._cmd.keys()): return False,False + if generic_cmd not in self._cmd: return False,False + # Is there value(s) passed ? + if len(cmd_splitted) > 2: values_to_set = cmd_splitted[2:] + # ex: return "set_radec", ["20:00:00", "90:00:00"] + return generic_cmd, values_to_set + + + def execute_cmd(self, raw_input_cmd:str)->GenericResult: + # GENERIC command + generic_cmd, values = self.is_generic_cmd(raw_input_cmd) + if generic_cmd is not False: + #print("GENERIC COMMAND") + return self.execute_generic_cmd(generic_cmd, values) + else: + ''' + if cmd.startswith('get_'): + #generic_cmd,_ = request[4:].split('(') + generic_cmd = cmd[4:] + if (generic_cmd not in self._cmd_getset.keys()) and (generic_cmd not in self._cmd_do.keys()): + #eval(request) + return self.get_radec() + print("cmd is", generic_cmd) + return self._get(generic_cmd) + return + ''' + # NATIVE command + #print("NATIVE COMMAND") + res_native = self.execute_native_cmd(raw_input_cmd) + return GenericResult(res_native) + + + #def execute_native_cmd(self, request:str, awaited_res_if_ok=None)->GenericResult: + def execute_native_cmd(self, native_cmd:str) -> str: + """ Execute a native command + + Args: + native_cmd: a native command + + Returns: + the command result + + """ + print("NATIVE Command to send is "+ repr(native_cmd)) + #self.send_request(native_cmd) + self.send_native_cmd(native_cmd) + native_res = self.receive_data() + return native_res + ''' + ok = True if not awaited_res_if_ok else (native_res == awaited_res_if_ok) + return GenericResult(native_res, ok) + ''' + + + ''' + def execute_native_cmd_OLD(self, request:str)->str: + self.send_request(request) + native_res = self.receive_data() + return native_res + ''' + + + def execute_unformated_native_cmd(self, request:str) -> str: + request = self.formated_cmd(request) + #return self.execute_native_cmd_OLD(request) + return self.execute_native_cmd(request) + + + def send_native_cmd(self, native_cmd:str)->str: + return self.send_data(native_cmd) + + + #@deprecated + def send_request(self, request:str)->str: + return self.send_native_cmd(request) + + + def print_available_commands(self): + print("\nAvailable commands are:") + print("- GET commands:") + print (list(cmd.replace('_',' ') for cmd in self._cmd.keys() if cmd.startswith('get_'))) + print("- SET commands:") + print (list(cmd.replace('_',' ') for cmd in self._cmd.keys() if cmd.startswith('set_'))) + print("- DO commands:") + print (list(cmd.replace('_',' ') for cmd in self._cmd.keys() if cmd.startswith('do_'))) + + + def available_commands(self): + return list(self._cmd.keys()) + + + # @abstract + def formated_cmd(self, cmd:str, value:str=None)->str: + return cmd + + + #def run_func(self, func, arg=None): + def run_func(self, func, *args): + #print("args", args) + if args: + return getattr(self, func)(*args) + else: + return getattr(self, func)() + + ''' + TELESCOPE COMMANDS (abstract methods) + ''' + + def execute_generic_cmd(self, generic_cmd:str, values_to_set:str=None)->str: + ''' Execute a generic command + + :param generic_cmd: str like "get_ra" or "set_ra" or "do_park"... + :param value: only for a "set_" cmd + ''' + + #log_d("\n\nGENERIC Command to send is "+generic_cmd) + print("\n\nGENERIC Command to send is "+generic_cmd) + # Check if generic_param exists + #if generic_cmd not in self._cmd.keys(): raise UnknownCommandException() + # if this generic command has no corresponding native command, raise NotImplementedError + native_cmd_infos = self._cmd[generic_cmd] + if not native_cmd_infos: raise NotImplementedError + # Get corresponding native command: + native_cmd = native_cmd_infos[0] + if not native_cmd: raise NotImplementedError + # ex: native_cmd == "do_init", "get_radec" + if native_cmd == generic_cmd: + #print("cmd,val", native_cmd, values_to_set) + #res:GenericResult = self.run_func(native_cmd, *values_to_set) + if values_to_set: + res = self.run_func(native_cmd, *values_to_set) + #res = getattr(self, native_cmd)(values_to_set) + else: + res = self.run_func(native_cmd) + #res = getattr(self, native_cmd)() + #if res is None: res = 'ok' + # res should be a GenericResult + if not isinstance(res,GenericResult): raise Exception("Should be a GenericResult", res) + return res + # ex: native_cmd == "GR" + else: + native_cmd = self.formated_cmd(native_cmd,values_to_set) + + awaited_res_if_ok = None + if len(native_cmd_infos) > 1: awaited_res_if_ok = native_cmd_infos[1] + #native_res = self.execute_native_cmd(self.formated_cmd(native_cmd,value), awaited_res_ok) + native_res = self.execute_native_cmd(native_cmd) + ok = True if not awaited_res_if_ok else (native_res == awaited_res_if_ok) + return GenericResult(native_res, ok) + + + + ''' + **************************** + **************************** + GENERIC TELESCOPE COMMANDS (abstract methods) + **************************** + **************************** + ''' + + + + ''' + **************************** + GENERIC GET & SET commands + **************************** + ''' + + + @generic_cmd + def get_timezone(self): pass + #def get_timezone(self): return self.execute_generic_cmd('get_timezone') + @generic_cmd + def set_timezone(self, hh): pass + #def set_timezone(self, hh): return self.execute_generic_cmd('set_timezone', hh) + + @generic_cmd + def get_date(self): pass + @generic_cmd + def set_date(self, mmddyy): pass + + @generic_cmd + def get_time(self): pass + @generic_cmd + def set_time(self, hhmmss): pass + + + + ''' + **************************** + GENERIC DO commands + **************************** + ''' + + # @abstract + #def do_INIT(self): return self._do("INIT") + + ''' do_PARK() (p103) + - STARTUP position = CWD + - :hC# + - position required for a Cold or Warm Start, pointing to the celestial pole of the given hemisphere (north or south), + with the counterweight pointing downwards (CWD position). From L4, V1.0 up + - HOME position parking => par defaut, c'est CWD, mais ca peut etre different + - :hP# + - defaults to the celestial pole visible at the given hemisphere (north or south) and can be set by the user + ''' + # @abstract + def do_PARK(self): pass + #def do_PARK(self): return self._do("PARK") + + # @abstract + def do_start(self): pass + def do_stop(self): pass + + # @abstract MACRO + def do_init(self): return NotImplementedError + + + + + +# TODO: empecher de creer une instance de cette classe abstraite +# Avec ABC ? + +''' +if __name__ == "__main__": + + #HOST, PORT = "localhost", 9999 + #HOST, PORT = "localhost", 20001 + HOST, PORT = "localhost", 11110 + + # Classic usage: + #tsock = SocketClient_UDP_TCP(HOST, PORT, "UDP") + # More elegant usage, using "with": + with SocketClient_ABSTRACT(HOST, PORT, "UDP") as tsock: + + # 0) CONNECT to server (only for TCP, does nothing for UDP) + tsock._connect_to_server() + + while True: + + # 1) SEND REQUEST data to server + # saisie de la requête au clavier et suppression des espaces des 2 côtés + data = input("REQUEST TO SERVER [ex: ':GD#' (Get Dec), ':GR#' (Get RA)']: ").strip() + # test d'arrêt + if data=="": break + #data_to_send = bytes(data + "\n", "utf-8") + tsock.send_data(data) + #mysock.sendto("%s" % data, (HOST, PORT)) + #print("Sent: {}".format(data)) + + # 2) RECEIVE REPLY data from server + data_received = tsock.receive_data() + #reponse, adr = mysock.recvfrom(buf) + #print("Received: {}".format(data_received)) + #print("Useful data received: {}".format(data_useful)) + print('\n') + + #tsock.close() +''' \ No newline at end of file diff --git a/src/device_controller/abstract_component/detector_sensor.py b/src/device_controller/abstract_component/detector_sensor.py new file mode 100755 index 0000000..b92cae3 --- /dev/null +++ b/src/device_controller/abstract_component/detector_sensor.py @@ -0,0 +1,78 @@ +#!/usr/bin/env python3 + +""" +Filter selector (abstract) implementation + +To be used as a base class (interface) for any concrete Filter selector class +""" + + +import sys + +# from sockets_tele/ +sys.path.append("..") +# from src_socket/client/ +#import src.core.pyros_django.utils.celme as celme + +sys.path.append("../../..") +import src.core.celme as celme + + +# Local application imports +#from device_controller.abstract_component.base import * +sys.path.append("../..") +from device_controller.abstract_component.base import * + + +# Default timeouts +TIMEOUT_SEND = 10 +TIMEOUT_RECEIVE = 10 + + + +#class SocketClientTelescopeAbstract(SocketClientAbstract): +class DeviceControllerDetectorSensor(DeviceControllerAbstract): + + # @abstract (to be overriden) + _cmd_device_concrete = {} + _cmd_device_abstract = {} + + + #TODO: remplacer PROTOCOL par "SOCKET-TCP", "SOCKET-UDP", "SERIAL", ou "USB" + def __init__(self, device_host:str="localhost", device_port:int=11110, PROTOCOL:str="TCP", buffer_size=1024, DEBUG=False): + ''' + :param device_host: server IP or hostname + :param device_port: server port + :param PROTOCOL: "UDP" or "TCP" + ''' + super().__init__(device_host, device_port, PROTOCOL, buffer_size, DEBUG) + # overwrite abstract _cmd dictionary with subclass native _cmd_native dictionary: + #self._cmd = {**self._cmd, **self._cmd_native} + + + + + ''' + **************************** + **************************** + GENERIC TELESCOPE COMMANDS (abstract methods) + **************************** + **************************** + ''' + + + + ''' + **************************** + GENERIC GET & SET commands + **************************** + ''' + + + ''' + **************************** + GENERIC DO commands + **************************** + ''' + + diff --git a/src/device_controller/abstract_component/detector_shutter.py b/src/device_controller/abstract_component/detector_shutter.py new file mode 100755 index 0000000..495b7d7 --- /dev/null +++ b/src/device_controller/abstract_component/detector_shutter.py @@ -0,0 +1,78 @@ +#!/usr/bin/env python3 + +""" +Filter selector (abstract) implementation + +To be used as a base class (interface) for any concrete Filter selector class +""" + + +import sys + +# from sockets_tele/ +sys.path.append("..") +# from src_socket/client/ +#import src.core.pyros_django.utils.celme as celme + +sys.path.append("../../..") +import src.core.celme as celme + + +# Local application imports +#from device_controller.abstract_component.base import * +sys.path.append("../..") +from device_controller.abstract_component.base import * + + +# Default timeouts +TIMEOUT_SEND = 10 +TIMEOUT_RECEIVE = 10 + + + +#class SocketClientTelescopeAbstract(SocketClientAbstract): +class DeviceControllerDetectorShutter(DeviceControllerAbstract): + + # @abstract (to be overriden) + _cmd_device_concrete = {} + _cmd_device_abstract = {} + + + #TODO: remplacer PROTOCOL par "SOCKET-TCP", "SOCKET-UDP", "SERIAL", ou "USB" + def __init__(self, device_host:str="localhost", device_port:int=11110, PROTOCOL:str="TCP", buffer_size=1024, DEBUG=False): + ''' + :param device_host: server IP or hostname + :param device_port: server port + :param PROTOCOL: "UDP" or "TCP" + ''' + super().__init__(device_host, device_port, PROTOCOL, buffer_size, DEBUG) + # overwrite abstract _cmd dictionary with subclass native _cmd_native dictionary: + #self._cmd = {**self._cmd, **self._cmd_native} + + + + + ''' + **************************** + **************************** + GENERIC TELESCOPE COMMANDS (abstract methods) + **************************** + **************************** + ''' + + + + ''' + **************************** + GENERIC GET & SET commands + **************************** + ''' + + + ''' + **************************** + GENERIC DO commands + **************************** + ''' + + diff --git a/src/device_controller/abstract_component/filter_selector.py b/src/device_controller/abstract_component/filter_selector.py new file mode 100755 index 0000000..dc95ba5 --- /dev/null +++ b/src/device_controller/abstract_component/filter_selector.py @@ -0,0 +1,78 @@ +#!/usr/bin/env python3 + +""" +Filter selector (abstract) implementation + +To be used as a base class (interface) for any concrete Filter selector class +""" + + +import sys + +# from sockets_tele/ +sys.path.append("..") +# from src_socket/client/ +#import src.core.pyros_django.utils.celme as celme + +sys.path.append("../../..") +import src.core.celme as celme + + +# Local application imports +#from device_controller.abstract_component.base import * +sys.path.append("../..") +from device_controller.abstract_component.base import * + + +# Default timeouts +TIMEOUT_SEND = 10 +TIMEOUT_RECEIVE = 10 + + + +#class SocketClientTelescopeAbstract(SocketClientAbstract): +class DeviceControllerFilterSelector(DeviceControllerAbstract): + + # @abstract (to be overriden) + _cmd_device_concrete = {} + _cmd_device_abstract = {} + + + #TODO: remplacer PROTOCOL par "SOCKET-TCP", "SOCKET-UDP", "SERIAL", ou "USB" + def __init__(self, device_host:str="localhost", device_port:int=11110, PROTOCOL:str="TCP", buffer_size=1024, DEBUG=False): + ''' + :param device_host: server IP or hostname + :param device_port: server port + :param PROTOCOL: "UDP" or "TCP" + ''' + super().__init__(device_host, device_port, PROTOCOL, buffer_size, DEBUG) + # overwrite abstract _cmd dictionary with subclass native _cmd_native dictionary: + #self._cmd = {**self._cmd, **self._cmd_native} + + + + + ''' + **************************** + **************************** + GENERIC TELESCOPE COMMANDS (abstract methods) + **************************** + **************************** + ''' + + + + ''' + **************************** + GENERIC GET & SET commands + **************************** + ''' + + + ''' + **************************** + GENERIC DO commands + **************************** + ''' + + diff --git a/src/device_controller/abstract_component/plc.py b/src/device_controller/abstract_component/plc.py new file mode 100755 index 0000000..dd905d1 --- /dev/null +++ b/src/device_controller/abstract_component/plc.py @@ -0,0 +1,133 @@ +#!/usr/bin/env python3 + +"""Socket Client Telescope (abstract) implementation + +To be used as a base class (interface) for any concrete socket client telescope class +""" + + +# Standard library imports +#from enum import Enum +import functools +import logging +import socket +import sys +import time + +# Third party imports + +# from sockets_tele/ +sys.path.append("..") +# from src_socket/client/ +sys.path.append("../../..") +#import src.core.pyros_django.utils.celme as celme +import src.core.celme as celme + + +# Local application imports +#sys.path.append('../..') +#from src.client.socket_client_abstract import UnknownCommandException, SocketClientAbstract +from src_device.client.base import * + + + + +# Execute also "set" and "do" commands +GET_ONLY=False +# Execute only "get" commands +#GET_ONLY=True + +# Default timeouts +TIMEOUT_SEND = 10 +TIMEOUT_RECEIVE = 10 + + +''' +class c(Enum): + + # GET, SET + DEC = 'DEC' + RA = 'RA' + RA_DEC = 'RA_DEC' + + # DO + PARK = 'PARK' + WARM_START = 'WARM_START' +''' + + + + + +class DeviceControllerPLC(DeviceControllerAbstract): + + # @abstract (to be overriden) + _cmd_native = {} + _cmd = { + # GET-SET commands: + 'get_ack': [], + + 'get_timezone': [], + 'set_timezone': [], + + 'get_date': [], + 'set_date': [], + + 'get_time': [], + 'set_time': [], + + # DO commands: + } + + + + def __init__(self, server_host:str="localhost", server_port:int=11110, PROTOCOL:str="TCP", buffer_size=1024, DEBUG=False): + ''' + :param server_host: server IP or hostname + :param server_port: server port + :param PROTOCOL: "UDP" or "TCP" + ''' + super().__init__(server_host, server_port, PROTOCOL, buffer_size, DEBUG) + # overwrite abstract _cmd dictionary with subclass native _cmd_native dictionary: + self._cmd = {**self._cmd, **self._cmd_native} + + + + + + + ''' + **************************** + **************************** + GENERIC PLC COMMANDS (abstract methods) + **************************** + **************************** + ''' + + + + ''' + **************************** + GENERIC GET & SET commands + **************************** + ''' + + # @abstract + @generic_cmd + def get_ack(self): pass + #return self.execute_generic_cmd("get_ack") + + + + ''' + **************************** + GENERIC DO commands + **************************** + ''' + + + + +# TODO: empecher de creer une instance de cette classe abstraite +# Avec ABC ? + diff --git a/src/device_controller/abstract_component/telescope.py b/src/device_controller/abstract_component/telescope.py new file mode 100755 index 0000000..4eb3e1b --- /dev/null +++ b/src/device_controller/abstract_component/telescope.py @@ -0,0 +1,569 @@ +#!/usr/bin/env python3 + +"""Socket Client Telescope (abstract) implementation + +To be used as a base class (interface) for any concrete socket client telescope class +""" + + +# Standard library imports +#from enum import Enum +import functools +import logging +import socket +import sys +import time + +# Third party imports + +# from sockets_tele/ +sys.path.append("..") +# from src_socket/client/ +sys.path.append("../../../..") +#import src.core.pyros_django.utils.celme as celme +import src.core.celme as celme + + +# Local application imports +#sys.path.append('../..') +#from src.client.socket_client_abstract import UnknownCommandException, SocketClientAbstract +##from src_socket.client.socket_client_abstract import * +from device_controller.abstract_component.base import * + + + + +# Execute also "set" and "do" commands +#GET_ONLY=False +# Execute only "get" commands +#GET_ONLY=True + +# Default timeouts +TIMEOUT_SEND = 10 +TIMEOUT_RECEIVE = 10 + + +''' +class c(Enum): + + # GET, SET + DEC = 'DEC' + RA = 'RA' + RA_DEC = 'RA_DEC' + + # DO + PARK = 'PARK' + WARM_START = 'WARM_START' +''' + + + + +class Position(): + x = 0 + y = 0 + def __init__(self, x,y): + self.x = x + self.y = y + def get_values(self): + return self.x, self.y + + + +#class SocketClientTelescopeAbstract(SocketClientAbstract): +#class TelescopeControllerAbstract(DeviceControllerAbstract): +class DeviceControllerTelescope(DeviceControllerAbstract): + + # @abstract (to be overriden) + _cmd_device_concrete = {} + _cmd_device_abstract = { + # GET-SET commands: + 'get_ack': [], + + 'get_ra': [], + 'set_ra': [], + 'get_dec': [], + 'set_dec': [], + 'get_radec': ['get_radec'], + 'set_radec': ['set_radec'], + + ''' + 'get_timezone': [], + 'set_timezone': [], + + 'get_date': [], + 'set_date': [], + + 'get_time': [], + 'set_time': [], + ''' + + 'get_longitude': [], + 'set_longitude': [], + 'get_latitude': [], + 'set_latitude': [], + + 'get_velocity': [], + 'set_velocity': [], + + # DO commands: + ##'do_init': ['do_init'], + ##'do_park': [], + 'do_goto': [], + 'do_move': [], + 'do_movenorth': [], + 'do_movesouth': [], + 'do_movewest': [], + 'do_moveeast': [], + 'do_move_dir': [], + 'do_warm_start': [], + 'do_prec_refr': [], + } + + + #TODO: remplacer PROTOCOL par "SOCKET-TCP", "SOCKET-UDP", "SERIAL", ou "USB" + def __init__(self, device_host:str="localhost", device_port:int=11110, PROTOCOL:str="TCP", buffer_size=1024, DEBUG=False): + ''' + :param device_host: server IP or hostname + :param device_port: server port + :param PROTOCOL: "UDP" or "TCP" + ''' + super().__init__(device_host, device_port, PROTOCOL, buffer_size, DEBUG) + # overwrite abstract _cmd dictionary with subclass native _cmd_native dictionary: + #self._cmd = {**self._cmd, **self._cmd_native} + + + + + def get_celme_longitude(self, longitude): + return celme.Angle(longitude).sexagesimal("d:+0180.0") + def get_celme_latitude(self, latitude): + return celme.Angle(latitude).sexagesimal("d:+090.0") + + + + ''' + TELESCOPE COMMANDS (abstract methods) + ''' + + + + ''' + **************************** + **************************** + GENERIC TELESCOPE COMMANDS (abstract methods) + **************************** + **************************** + ''' + + + + ''' + **************************** + GENERIC GET & SET commands + **************************** + ''' + + # @abstract + @generic_cmd + def get_ack(self): pass + #return self.execute_generic_cmd("get_ack") + + # RA/DEC + # @abstract + ''' + Sets the object's Right Ascension and the object status to "Not Selected". + The :Sd# command has to follow to complete the selection. + The subsequent use of the :ON...# command is recommended (p106) + :Sr:.# + or + :Sr::# + 0 if invalid + 1 if valid + ''' + @generic_cmd + def get_ra(self): pass + #def get_ra(self): return self.execute_generic_cmd("get_ra") + @generic_cmd + def set_ra(self, ra): pass + #return self._set("ra", ra) + + ''' + Sets the object's declination. + It is important that the :Sr# command has been send prior. + Internal calculations are done that may take up to 0.5 seconds. + If the coordinate selection is valid the object status is set to "Selected" + :Sd{+-}
{*°}# + or + :Sd{+- }
{*°:}: + 0 if invalid + 1 if valid + ''' + @generic_cmd + def get_dec(self): pass + #def get_dec(self): return self.execute_generic_cmd("get_dec") + @generic_cmd + def set_dec(self, dec): pass + #def set_dec(self, dec): return self._set("dec", dec) + + # MACRO radec + #def get_radec(self): return self._get("RADEC") + #def get_radec(self)->tuple: return ((self.get_ra()), (self.get_dec())) + def get_radec(self)->GenericResult: + return GenericResult(self.get_ra().txt + "," + self.get_dec().txt) + + # MACRO + def set_radec(self, ra, dec)->GenericResult: + self.set_ra(ra) + self.set_dec(dec) + return GenericResult("OK") + + @generic_cmd + def get_long(self): pass + @generic_cmd + def set_long(self, longitude): pass + + @generic_cmd + def get_lat(self): pass + @generic_cmd + def set_lat(self, latitude): pass + + @generic_cmd + def get_vel(self): pass + + + + + ''' + **************************** + GENERIC DO commands + **************************** + ''' + + # @abstract + #def do_INIT(self): return self._do("INIT") + + ''' do_PARK() (p103) + - STARTUP position = CWD + - :hC# + - position required for a Cold or Warm Start, pointing to the celestial pole of the given hemisphere (north or south), + with the counterweight pointing downwards (CWD position). From L4, V1.0 up + - HOME position parking => par defaut, c'est CWD, mais ca peut etre different + - :hP# + - defaults to the celestial pole visible at the given hemisphere (north or south) and can be set by the user + ''' + + @generic_cmd + def do_move(self): pass + @generic_cmd + def do_movenorth(self): pass + @generic_cmd + def do_movesouth(self): pass + @generic_cmd + def do_movewest(self): pass + @generic_cmd + def do_moveeast(self): pass + + # @abstract + #def do_GOTO(self, pos:Position): return self._do("GOTO") + #def do_WARM_START(self): return self._do("WARM_START") + @generic_cmd + def do_warm_start(self): pass + + @generic_cmd + def do_prec_refr(self): pass + + + + # MACRO generic command + def do_init(self): + + ''' + 1) Send cde ACK ('06') and check answer to see if telescope is ready (see doc page 100) + (utile pour savoir si tout est ok ; par ex, si une raquette est branchée sur le tele, ça peut bloquer le protocole) + Usable for testing the serial link and determining the type of mount (German equatorial). + Return code can be: + - B# while the initial startup message is being displayed (new in L4), + - b# while waiting for the selection of the Startup Mode, + - S# during a Cold Start (new in L4), + - G# after completed startup ==> MEANS ALL IS OK + ''' + #ACK = self.get("ACK") + ACK = self.get_ack() + + ''' + 2) IF telescope is not ready (still starting up), ask it to do a Warm Start ('bW#') + During Startup, with a "b#" being returned, the PC can select the startup mode by sending a + • bC# for selecting the Cold Start, + • bW# for selecting the Warm Start, + • bR# for selecting the Warm Restart + If not ok (still starting up, no 'G#' in return), send 'bW#' (see above) for selecting the Warm Start + ''' + #if ACK != 'G': + if not ACK.ok: + self.do_warm_start() + ACK = self.get_ack() + elapsed_time = 0 + while not ACK.ok: + time.sleep(1) + elapsed_time += 1 + if elapsed_time == TIMEOUT_RECEIVE: raise TimeoutException() + ACK = self.get_ack() + + + ''' + 3) Set timezone, date, and time (p109) + ''' + + ''' + a) set TIMEZONE + Set the number of hours by which your local time differs from UTC. + If your local time is earlier than UTC set a positive value, + if later than UTC set a negative value. The time difference has to be set before setting the calendar date (SC) and local time (SL), since the Real Time Clock is running at UTC + => :SG{+-}hh# + ''' + res = self.get_timezone() + print("Current timezone is", res) + res = self.set_timezone('+00') + #if res != '1': raise UnexpectedCommandReturnCode(res) + if not res.ok: raise UnexpectedCommandReturnCode(res) + res = self.get_timezone() + if res.txt != '+00': raise UnexpectedCommandReturnCode(res) + print("NEW timezone set is", res) + + + ''' + b) set DATE + Set Calendar Date: + months mm, days dd, year yy of the civil time according to the timezone set. + The internal calendar/clock uses GMT + :SC/
/# + 0 if invalid + or + TODO: + 1Updating planetary data#<24 blanks># + ''' + res = self.get_date() + print("Current date is", res) + # format is 2018-09-26T17:50:21 + d = self.get_utc_date() + # format to mm/dd/yy + now_utc_mm_dd_yy = d[5:7] + '/' + d[8:10] + '/' + d[2:4] + #print("date is", now_utc_mm_dd_yy) + res = self.set_date(now_utc_mm_dd_yy) + #res = self.set_DATE(self.get_utc_date()) + #if res[0] != '1': raise UnexpectedCommandReturnCode(res) + #if not res.startswith('1Updating planetary data'): raise UnexpectedCommandReturnCode(res) + if not res.ok: raise UnexpectedCommandReturnCode(res) + res = self.get_date() + if res.txt != now_utc_mm_dd_yy: raise UnexpectedCommandReturnCode(res) + print("NEW DATE set is", res) + + ''' + c) set TIME + Set RTC Time from the civil time hours hh, minutes mm and seconds ss. + The timezone must be set before using this command + :SL::# + ''' + res = self.get_time() + print("Current time is", res) + _,now_utc_hh_mm_ss = d.split('T') + #print("time is", now_utc_hh_mm_ss[:5]) + res = self.set_time(now_utc_hh_mm_ss) + #if res != '1': raise UnexpectedCommandReturnCode(res) + if not res.ok: raise UnexpectedCommandReturnCode(res) + res = self.get_time() + if res.txt[:5] != now_utc_hh_mm_ss[:5]: raise UnexpectedCommandReturnCode(res) + print("NEW TIME set is", res) + + + ''' + 4) Set LOCATION (lat,long) (p103,110) + Pour l'observatoire de Guitalens: + Sg = 2.0375 E + St = 43.6443 N + (attention, 2.0375 E = - 2.0375) + ''' + + ''' + a) set Longitude + Sets the longitude of the observing site to ddd degrees and mm minutes. + The longitude has to be specified positively for western latitudes + (west of Greenwich, the plus sign may be omitted) and negatively for eastern longitudes. + Alternatively, 360 degrees may be added to eastern longitudes. + => :Sg{+-}*# + ''' + # TELE format is -002°02 (I convert it to -002:02) + res = self.get_long() + print("Current longitude is", res) + + # CELME format is -002:02:15 + res = self.get_celme_longitude("-2.0375") + res_ddd_mm = res[:-3] + #res_ddd_mm = res[:-3].replace(':','*') + #res_ddd_mm = '-002:03' + + #print("celme longitude is", res) + ddd,mm,ss = res.split(':') + #dddmm = '-002*03' + res = self.set_long(ddd+'*'+mm) + #if res != '1': raise UnexpectedCommandReturnCode(res) + if not res.ok: raise UnexpectedCommandReturnCode(res) + res = self.get_long() + if res.txt != res_ddd_mm: raise UnexpectedCommandReturnCode(res_ddd_mm, res.txt) + print("NEW longitude set is", res) + + ''' + b) set Latitude + Sets the latitude of the observing site to dd degrees, mm minutes. + The minus sign indicates southern latitudes, the positive sign may be omitted. + => :St{+-}
*# + ''' + # TELE format is +43°38 (I convert it to +43:38) + res = self.get_lat() + print("Current latitude is", res) + + # CELME format is +43:38:15 + res = self.get_celme_latitude("+43.6443") + res_dd_mm = res[:-3] + #res_dd_mm = res[:-3].replace(':','*') + print("res is", res) + #res_dd_mm = '+43:50' + + #print("celme longitude is", res) + dd,mm,ss = res.split(':') + ddmm = dd+'*'+mm + #ddmm = '+43*50' + res = self.set_lat(ddmm) + #if res != '1': raise UnexpectedCommandReturnCode(res) + if not res.ok: raise UnexpectedCommandReturnCode(res) + res = self.get_lat() + if res.txt != res_dd_mm: raise UnexpectedCommandReturnCode(res_dd_mm,res.txt) + print("NEW latitude set is", res) + + + ''' + 5) Send cde ':p3#' : Precession & Refraction (see page 107) + Ask Gemini to do Precession calculation + Coordinates transferred to the Gemini refer to the standard epoch J2000.0. + Refraction is calculated (From L4, V1.0 up) + ''' + self.do_prec_refr() + + return GenericResult("OK") + + + # @abstract + def set_speed(self, speed_rate): + pass + + + ''' GOTO (p105) + - GOTO(position, blocking=Y/N): + (MS = move start) + = Goto RA=18h23m45s Dec=+34d00m00s J2000 + - radec.goto() + ''' + # MACRO generic command + def do_goto(self, ra, dec, speed_rate=None): + + # 1) set speed + if speed_rate: self.set_speed(speed_rate) + + radec = self.get_radec() + print("Current position is", radec) + + # 2) set RA-DEC + ''' + :Sr18:23:45#:Sd+34:00:00#:MS# + ''' + res = self.set_ra(ra) + #if res != '1': raise UnexpectedCommandReturnCode(res) + if res.ko: raise UnexpectedCommandReturnCode(res) + res = self.set_dec(dec) + #if res != '1': raise UnexpectedCommandReturnCode(res) + if res.ko: raise UnexpectedCommandReturnCode(res) + + # 3) MOVE (non blocking by default for Gemini) + self.do_move() + + # 4) Test velocity until it is "Tracking" + ''' + After MOVE, test velocity with ':Gv#' (p103) : we should have 'S', then 'C', then 'T' + - N (for "no tracking") + - T (for Tracking) + - G (for Guiding) + - C (for Centering) + - S (for Slewing) + ''' + vel = None + while vel != 'T': + v = self.get_vel() + vel = v.txt + print("Velocity is", v) + time.sleep(2) + + time.sleep(2) + radec= self.get_radec() + print("Current position is", radec) + + return GenericResult("OK") + + + # @abstract MACRO + def do_move_dir(self, dir, nbsec, speed_rate=None): + dir = dir.upper() + if speed_rate: self.set_speed(speed_rate) + if dir=="NORTH": self.do_movenorth() + elif dir=="SOUTH": self.do_movesouth() + elif dir=="WEST": self.do_movewest() + elif dir=="EAST": self.do_moveeast() + else: raise UnknownCommandException(dir) + time.sleep(int(nbsec)) + self.do_stop() + return GenericResult("OK") + + + + + + +# TODO: empecher de creer une instance de cette classe abstraite +# Avec ABC ? + +''' +if __name__ == "__main__": + + #HOST, PORT = "localhost", 9999 + #HOST, PORT = "localhost", 20001 + HOST, PORT = "localhost", 11110 + + # Classic usage: + #tsock = SocketClient_UDP_TCP(HOST, PORT, "UDP") + # More elegant usage, using "with": + with SocketClient_ABSTRACT(HOST, PORT, "UDP") as tsock: + + # 0) CONNECT to server (only for TCP, does nothing for UDP) + tsock._connect_to_server() + + while True: + + # 1) SEND REQUEST data to server + # saisie de la requête au clavier et suppression des espaces des 2 côtés + data = input("REQUEST TO SERVER [ex: ':GD#' (Get Dec), ':GR#' (Get RA)']: ").strip() + # test d'arrêt + if data=="": break + #data_to_send = bytes(data + "\n", "utf-8") + tsock.send_data(data) + #mysock.sendto("%s" % data, (HOST, PORT)) + #print("Sent: {}".format(data)) + + # 2) RECEIVE REPLY data from server + data_received = tsock.receive_data() + #reponse, adr = mysock.recvfrom(buf) + #print("Received: {}".format(data_received)) + #print("Useful data received: {}".format(data_useful)) + print('\n') + + #tsock.close() +''' \ No newline at end of file diff --git a/src/device_controller/channels/client_channel.py b/src/device_controller/channels/client_channel.py new file mode 100755 index 0000000..43ae9a8 --- /dev/null +++ b/src/device_controller/channels/client_channel.py @@ -0,0 +1,118 @@ +#!/usr/bin/env python3 + +"""Socket Client (abstract) + +To be used as a base class (interface) for any concrete socket client class +""" + + +# Standard library imports + +# Third party imports +# None + +# Local application imports +from device_controller.logs import * + + + +##class SocketClientAbstract(): +class ClientChannel(): + + my_channel = None + buf = 1024 + + def __init__(self, server_host:str="localhost", server_port:int=11110, PROTOCOL:str="SOCKET-TCP", buffer_size=1024, DEBUG=False): + ''' + :param server_host: server IP or hostname + :param server_port: server port + :param PROTOCOL: "SOCKET-TCP" or "SOCKET-UDP" or "SERIAL" or "USB" (to be continued...) + ''' + self.DEBUG = DEBUG + + self.HOST = server_host + self.PORT = server_port + self.PROTOCOL = PROTOCOL + self.buf = buffer_size + + # Logger configuration + #self.set_logger() + ##set_logger(DEBUG) + log_d("\n**************************") + log_d("Client CHANNEL instanciated") + + + + # So that we can use this with the "with" statement (context manager) + def __enter__(self): + return self + def __exit__(self, type, value, traceback): + self.close() + #log_d("Client channel killed") + print("Client channel killed") + + + def send_data(self, data:str): + ##data_encapsulated = self.encapsulate_data_to_send(data) + self._send_data(data) + ''' + #data_to_send = bytes(data_to_send + "\n", "utf-8") + #data_to_send = bytes(data_to_send + "\x00", "utf-8") + ##data_to_send = bytes(data_to_send, "utf-8") + data_to_send_bytes = data_to_send.encode("utf-8") + #data_to_send_bytes = data_to_send.encode() + if self.PROTOCOL=="SOCKET-TCP": + # Unlike send(), this method sendall() continues to send data from bytes until either all data has been sent or an error occurs. + # None is returned on success. + # On error, an exception is raised, and there is no way to determine how much data, if any, was successfully sent + nb_bytes_sent = self.mysock.sendall(data_to_send_bytes) + else: + # SOCKET-UDP + nb_bytes_sent = self.mysock.sendto(data_to_send_bytes, (self.HOST, self.PORT)) + log_i(f'Sent: {data_to_send_bytes}') + log_i(f"Sent {nb_bytes_sent} bytes") + ''' + #@abstract + def _send_data(self, data_to_send:str): + pass + + def receive_data(self)->str: + data_received = self._receive_data() + ##data_received_bytes = self.mysock.recv(self.buf) + #log_d("Received (all data): {}".format(data_received)) + #log_d("data in bytes: "+str(bytes(data_received, "utf-8"))) + ##data_received_uncaped = self.uncap_received_data(data_received) + #log_i("RECEIVED (useful data): {}".format(data_received)) + return data_received + #@abstract + def _receive_data(self)->bytes: + pass + ''' + data_received = self.mysock.recv(self.buf) + #log_d("data type is "+str(type(data_received))) + log_i(f"RECEIVED (ALL BYTES): {data_received}") + #return str(data_received, "utf-8") + return data_received + ''' + + #@abstract + def _connect_to_server(self): + pass + + # Close socket + def close(self): + self.mysock.close() + + + ''' + put(), read(), put_read() commands + ''' + def put(self, data:str): + self.send_data(data) + def read(self)->str: + return self.receive_data() + def put_read(self, data:str)->str: + # send command + self.put(data) + # receive answer (or ack) + return self.read() diff --git a/src/device_controller/channels/client_channel_serial.py b/src/device_controller/channels/client_channel_serial.py new file mode 100755 index 0000000..8d3eea8 --- /dev/null +++ b/src/device_controller/channels/client_channel_serial.py @@ -0,0 +1,51 @@ +#!/usr/bin/env python3 + +"""SERIAL Client implementation + +""" + + +# Standard library imports + +# Third party imports +# None + +# Local application imports +from device_controller.channels.client_channel import * + + + + +class ClientChannelSerial(ClientChannel): + + buf = 1024 + + + def __init__(self, server_host:str="localhost", server_port:int=11110, buffer_size=1024, DEBUG:bool=False): + ''' + :param server_host: server IP or hostname + :param server_port: server port + ''' + #TODO: set a protocol + self.protocol = None + super().__init__(server_host, server_port, "SERIAL", buffer_size, DEBUG) + + + + #@override + def _connect_to_server(self): + pass + + # Close socket + def close(self): + self.mysock.close() + + #@override + def _send_data(self, data_to_send:str): + pass + + #@override + def _receive_data(self)->str: + data_received = self.mysock.recv(self.buf) + return data_received + diff --git a/src/device_controller/channels/client_channel_socket.py b/src/device_controller/channels/client_channel_socket.py new file mode 100755 index 0000000..596bd7c --- /dev/null +++ b/src/device_controller/channels/client_channel_socket.py @@ -0,0 +1,181 @@ +#!/usr/bin/env python3 + +"""Socket Client implementation (TCP or UDP) + +""" + + +# Standard library imports +import socket + +# Third party imports +# None + +# Local application imports +from device_controller.channels.client_channel import * + + + + +##class SocketClientAbstract(): +class ClientChannelSocket(ClientChannel): + + # Select protocol here (udp or tcp) + buf = 1024 + mysock = None + + # STAMP : + # 00 00 00 00 00 00 00' (8 bytes from '00' to 'FF', 1st one is LOW BYTE) + # ex: 01 + STAMP = 01 00 00 00 00 00 00 00' (8 bytes from '00' to 'FF', 1st one is LOW BYTE) + #STAMP_FILLER = '00000000000000' + ''' + STAMP_FILLER = '000000' + MYSTAMP = '01' + MY_FULL_STAMP = MYSTAMP + STAMP_FILLER + ''' + + + + def __init__(self, server_host:str="localhost", server_port:int=11110, PROTOCOL:str="SOCKET-TCP", buffer_size=1024, DEBUG=False): + ''' + :param server_host: server IP or hostname + :param server_port: server port + :param PROTOCOL: "SOCKET-UDP" or "SOCKET-TCP" + ''' + myprotocol = socket.SOCK_DGRAM if PROTOCOL=="SOCKET-UDP" else socket.SOCK_STREAM + self.mysock = socket.socket(socket.AF_INET, myprotocol) + super().__init__(server_host, server_port, PROTOCOL, buffer_size, DEBUG) + # Logger configuration + #self.set_logger() + ''' + log_d("\n**************************") + log_d("Logger configured") + log_d("Client instanciated") + ''' + + + ''' + def _send_data(self, data_to_send:str): + #data_to_send = bytes(data_to_send + "\n", "utf-8") + #data_to_send = bytes(data_to_send + "\x00", "utf-8") + ##data_to_send = bytes(data_to_send, "utf-8") + #print("before sending", data_to_send) + data_to_send_bytes = data_to_send.encode("utf-8") + #data_to_send_bytes = data_to_send.encode() + #print("before sending", data_to_send_bytes) + if self.PROTOCOL=="SOCKET-TCP": + # Unlike send(), this method sendall() continues to send data from bytes until either all data has been sent or an error occurs. + # None is returned on success. + # On error, an exception is raised, and there is no way to determine how much data, if any, was successfully sent + nb_bytes_sent = self.mysock.sendall(data_to_send_bytes) + else: + nb_bytes_sent = self.mysock.sendto(data_to_send_bytes, (self.HOST, self.PORT)) + log_i(f'Sent: {data_to_send_bytes}') + log_i(f"Sent {nb_bytes_sent} bytes") + ''' + ''' + def _receive_data(self)->bytes: + data_received = self.mysock.recv(self.buf) + #log_d("data type is "+str(type(data_received))) + log_i(f"RECEIVED (ALL BYTES): {data_received}") + #return str(data_received, "utf-8") + return data_received + ''' + + # Only for TCP ; does nothing for UDP + #@override + def _connect_to_server(self): + if self.PROTOCOL=="SOCKET-TCP": self.mysock.connect((self.HOST, self.PORT)) + print(f"Ready to send commands to HOST {self.HOST} on PORT {self.PORT} \n") + + + # Close socket + def close(self): + self.mysock.close() + + #@override + def _send_data(self, data_to_send:str): + #data_to_send = bytes(data_to_send + "\n", "utf-8") + #data_to_send = bytes(data_to_send + "\x00", "utf-8") + ##data_to_send = bytes(data_to_send, "utf-8") + data_to_send_bytes = data_to_send.encode("utf-8") + #data_to_send_bytes = data_to_send.encode() + if self.PROTOCOL=="SOCKET-TCP": + # Unlike send(), this method sendall() continues to send data from bytes until either all data has been sent or an error occurs. + # None is returned on success. + # On error, an exception is raised, and there is no way to determine how much data, if any, was successfully sent + nb_bytes_sent = self.mysock.sendall(data_to_send_bytes) + else: + # SOCKET-UDP + nb_bytes_sent = self.mysock.sendto(data_to_send_bytes, (self.HOST, self.PORT)) + log_i(f'Sent: {data_to_send_bytes}') + log_i(f"Sent {nb_bytes_sent} bytes") + + #@override + def _receive_data(self)->str: + data_received_bytes = self.mysock.recv(self.buf) + #TODO: resoudre ce pb plus proprement (utiliser unicode au lieu de utf-8 codec ??? + # BUGFIX: b'\xdf' (should correspond to "°" symbol) generates a UnicodeDecodeError, + # so, replace it by ':' (b'\x3A') + if b'\xdf' in data_received_bytes: + data_received_bytes = data_received_bytes.replace(b'\xdf', b'\x3A') + log_i(f"RECEIVED (ALL BYTES): {data_received_bytes}") + + data_received = data_received_bytes.decode() + #log_d("data type is "+str(type(data_received))) + #return str(data_received, "utf-8") + return data_received + + ''' + # Encapsulate useful data to be ready for sending + #@override + def encapsulate_data_to_send(self, data:str): + #return bytes(data, "utf-8") + return data.encode("utf-8") + # Extract data from received raw data + #@override + def uncap_received_data(self, data_received_bytes:bytes): + #return data_received.decode("utf-8) + return data_received_bytes.decode() + ''' + + +# TODO: empecher de creer une instance de cette classe abstraite +# Avec ABC ? + +''' +if __name__ == "__main__": + + #HOST, PORT = "localhost", 9999 + #HOST, PORT = "localhost", 20001 + HOST, PORT = "localhost", 11110 + + # Classic usage: + #tsock = SocketClient_UDP_TCP(HOST, PORT, "UDP") + # More elegant usage, using "with": + with SocketClient_ABSTRACT(HOST, PORT, "UDP") as tsock: + + # 0) CONNECT to server (only for TCP, does nothing for UDP) + tsock._connect_to_server() + + while True: + + # 1) SEND REQUEST data to server + # saisie de la requête au clavier et suppression des espaces des 2 côtés + data = input("REQUEST TO SERVER [ex: ':GD#' (Get Dec), ':GR#' (Get RA)']: ").strip() + # test d'arrêt + if data=="": break + #data_to_send = bytes(data + "\n", "utf-8") + tsock.send_data(data) + #mysock.sendto("%s" % data, (HOST, PORT)) + #print("Sent: {}".format(data)) + + # 2) RECEIVE REPLY data from server + data_received = tsock.receive_data() + #reponse, adr = mysock.recvfrom(buf) + #print("Received: {}".format(data_received)) + #print("Useful data received: {}".format(data_useful)) + print('\n') + + #tsock.close() +''' \ No newline at end of file diff --git a/src/device_controller/channels/client_channel_usb.py b/src/device_controller/channels/client_channel_usb.py new file mode 100755 index 0000000..34f3d08 --- /dev/null +++ b/src/device_controller/channels/client_channel_usb.py @@ -0,0 +1,51 @@ +#!/usr/bin/env python3 + +"""SERIAL Client implementation + +""" + + +# Standard library imports + +# Third party imports +# None + +# Local application imports +from device_controller.channels.client_channel import * + + + + +class ClientChannelUSB(ClientChannel): + + buf = 1024 + + + def __init__(self, server_host:str="localhost", server_port:int=11110, buffer_size=1024, DEBUG=False): + ''' + :param server_host: server IP or hostname + :param server_port: server port + ''' + #TODO: set a protocol + self.protocol = None + super().__init__(server_host, server_port, "USB", buffer_size, DEBUG) + + + + #@override + def _connect_to_server(self): + pass + + # Close socket + def close(self): + self.mysock.close() + + #@override + def _send_data(self, data_to_send:str): + pass + + #@override + def _receive_data(self)->str: + data_received = self.mysock.recv(self.buf) + return data_received + diff --git a/src/device_controller/concrete_component/ak/.emptyfile b/src/device_controller/concrete_component/ak/.emptyfile new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/src/device_controller/concrete_component/ak/.emptyfile diff --git a/src/device_controller/concrete_component/ak/ak_plc_controller.py b/src/device_controller/concrete_component/ak/ak_plc_controller.py new file mode 100755 index 0000000..706424f --- /dev/null +++ b/src/device_controller/concrete_component/ak/ak_plc_controller.py @@ -0,0 +1,259 @@ +#!/usr/bin/env python3 + +"""Socket Client Gemini Telescope implementation +To be used as a concrete class to system control a Gemini telescope +""" + +# Standard library imports +#import socket +#import logging +import sys +import time + +# Third party imports +# None + +# Local application imports +sys.path.append('../..') +#from src.client.socket_client_telescope_abstract import Position, UnknownCommandException, TimeoutException, SocketClientTelescopeAbstract +from src_device.client.plc_controller_abstract import * + +# Default timeouts +TIMEOUT_SEND = 10 +TIMEOUT_RECEIVE = 10 + +# COMMON CONSTANTS WITH SERVER +TERMINATOR = '\x00' +COMMAND5 = '050000' +#COMMAND6_SIMPLE = '\x00\x06' +COMMAND6 = '\x00\x06\x00' +COMMAND6_SIMPLE = '6' + + + + +class DeviceControllerPLCAK(DeviceControllerPLC): + + # STAMP : + # 00 00 00 00 00 00 00' (8 bytes from '00' to 'FF', 1st one is LOW BYTE) + # ex: 01 + STAMP = 01 00 00 00 00 00 00 00' (8 bytes from '00' to 'FF', 1st one is LOW BYTE) + #STAMP_FILLER = '00000000000000' + #MY_FULL_STAMP = MYSTAMP + STAMP_FILLER + + # Initialize request number (will be increased at each request) + request_num = 0 + # Request number stands on 4 digits (from 0001 to 9999) + request_num_nb_digits = 4 + # For 4 digits, we should get the format '{:04d}': + request_num_format = '{:0'+str(request_num_nb_digits)+'d}' + # Stamp filler with 0 + STAMP_FILLER = '0' * (8 - request_num_nb_digits) + + #data = " ".join(sys.argv[1:]) + #data_to_send = bytes(data + "\n", "utf-8") + + ''' Commands dictionary + NEW foramt is: + 'generic-command-name': ['native-command-name', 'awaited_result_if_ok', 'other-awaited-results if not ok...], + + Old format was: + 'CMD-NAME': ['cmd-get', 'awaited_res_ok_for_get', 'cmd-set', 'awaited_res_ok_for_set', 'comment'], + ''' + # @overwrite + _cmd_native = { + # GET @ SET commands + 'get_ack': [COMMAND6, 'G', 'B','b','S'], # B# while the initial startup message is being displayed (new in L4), b# while waiting for the selection of the Startup Mode, S# during a Cold Start (new in L4), G# after completed startup. + + # RA-DEC (p109-110) + #:Sr:.# or :Sr::# + #Sets the object's Right Ascension and the object status to "Not Selected". The :Sd# command has to follow to complete the selection. The subsequent use of the :ON...# command is recommended + #:Sd{+-}
{*°}# or :Sd{+- }
{*°:}: + #Sets the object's declination. It is important that the :Sr# command has been send prior. Internal calculations are done that may take up to 0.5 seconds. If the coordinate selection is valid the object status is set to "Selected" + #'RADEC': [('GR','GD'), ''], + #'ra': ['GR', None, 'Sr', None, 'commentaire'], + 'get_ra': ['GR'], + 'set_ra': ['Sr'], + 'get_dec': ['GD'], + 'set_dec': ['Sd'], + # get_radec and set_radec are already defined in abstract class + + 'get_timezone': ['GG'], + 'set_timezone': ['SG', '1'], + + # ALT-AZ (p?) + "get_alt": ['GA'], + "get_az": ['GZ'], + #"ALT-AZ": [('GA','GZ'), ''], + + # LONG-LAT + "get_long": ['Gg'], + "set_long": ['Sg'], + "get_lat": ['Gt'], + "set_lat": ['St'], + #"LONGLAT": [('Gg','Gt'), ''], + + "get_hangle": ['GH'], + + 'get_vel': ['Gv'], + #"get_maxvel": ['Gv'], + + 'get_date': ['GC'], + 'set_date': ['SC'], + 'get_time': ['GL'], + 'set_time': ['SL'], + + # DO commands + # defined in abstract class: + #'do_init': ['do_init'], + 'do_park': ['hP'], + 'do_warm_start': ['bW'], + 'do_prec_refr': ['p3'], + 'do_move': ['MS', '0', '1Object below horizon.', '2No object selected.', '3Manual Control.', '4Position unreachable.', '5Not aligned.', '6Outside Limits.' ], + 'do_movenorth': ['Mn'], + 'do_movesouth': ['Ms'], + 'do_movewest': ['Mw'], + 'do_moveeast': ['Me'], + 'do_stop': ['Q'], + } + + + # @overwrite + # Gemini is using UDP + def __init__(self, server_host:str="localhost", server_port:int=11110, DEBUG=False): + super().__init__(server_host, server_port, "TCP", 1024, DEBUG) + + + # @overwrite + def formated_cmd(self, cmd:str, values_to_set:str=None)->str: + if values_to_set != None: + for value_to_set in values_to_set: + cmd += value_to_set + if cmd not in (COMMAND6, COMMAND5): + cmd += '#' + if cmd not in ('bC#','bW#','bR#'): + cmd=':'+cmd + return cmd + + + # @overwrite + def encapsulate_data_to_send(self, command:str): + r''' Encapsulate useful data to be ready for sending + + If data is "complete" (with stamp, and well formatted), send it as is + Otherwise, add stamp and format it + + 3 types of commands: + - TYPE1: '06' or '050000' + - TYPE2: ':cde#' (mainly ':??#' - i.e : ':GD#', ':GR#', ...) + - TYPE3: 'b?#' (bC# = Cold Start, bW# = selecting Warm Start, bR# = selecting Warm Restart) + + :Examples: + >>> tele = SocketClientTelescopeGemini("localhost", 11110, DEBUG=False) + >>> tele.encapsulate_data_to_send(':GD#') + '00010000:GD#\x00' + >>> tele.encapsulate_data_to_send(':GR#') + '00020000:GR#\x00' + >>> tele.close() + + # ne marche pas => '00010000:GD#\x00' + ''' + + ####return bytes(self.MYSTAMP + self.STAMP_FILLER + data + "\n", "utf-8") + + # TYPE1 + #print("command to encapsulate is", repr(command)) + + if command == COMMAND6_SIMPLE: command = COMMAND6 + + if command not in (COMMAND6, COMMAND5): + # TYPE2 or 3 + #has_starting_car = data.find(':') > -1 + if len(command) < 3: raise UnknownCommandException() + if not (command[-1]=='#'): raise UnknownCommandException() + if not (command[0]==':') and command not in ('bC#','bW#','bR#'): raise UnknownCommandException() + #stamp,command = data.split(':',1) + + #if command.find('#')>-1: command,_ = command.split('#',1) + self.request_num += 1 + # Format to request_num_nb_digits (4) digits + request_num_str = self.request_num_format.format(self.request_num) + self.last_stamp = request_num_str + self.STAMP_FILLER + + #if command == COMMAND6_SIMPLE: command = COMMAND6_REAL + return self.last_stamp + command + TERMINATOR + #return bytes(data + "\n", "utf-8") + ####return bytes(self.MYSTAMP + self.STAMP_FILLER + data + "\n", "utf-8") + + # @overwrite + def uncap_received_data(self, data_received_bytes:bytes)->str: + r""" + Extract useful data from received raw data + + >>> tele = SocketClientTelescopeGemini("localhost", 11110, DEBUG=False) + >>> tele.last_stamp = '00170000' + >>> tele.uncap_received_data(b'001700001\x00') + '1' + >>> tele.close() + """ + + #print("data_received_bytes type is", type(data_received_bytes)) + #print("data received is", data_received_bytes) + + #TODO: resoudre ce pb plus proprement (utiliser unicode au lieu de utf-8 codec ??? + # BUGFIX: b'\xdf' (should correspond to "°" symbol) generates a UnicodeDecodeError, + # so, replace it by ':' (b'\x3A') + if b'\xdf' in data_received_bytes: + data_received_bytes = data_received_bytes.replace(b'\xdf', b'\x3A') + + data_received = data_received_bytes.decode() + #print("data_received is", data_received) + # Remove STAMP (and \n at the end): + #useful_data = data_received.split(self.MY_FULL_STAMP)[1][:-1] + useful_data = data_received.split(self.last_stamp)[1][:-1] + # Remove '#' at the end, if exists + if useful_data[-1] == '#': useful_data = useful_data[:-1] + return useful_data + + + + +if __name__ == "__main__": + + import doctest + doctest.testmod() + exit() + + # Classic usage: + #plc_client = SocketClient_Gemini(HOST, PORT) + # More elegant usage, using "with": + with DeviceControllerPLCAK("localhost", 11110, DEBUG=False) as plc_client: + + # 0) CONNECT to server (only for TCP, does nothing for UDP) + plc_client._connect_to_server() + + #plc_client.config() + + # Send some commands to the Telescope + #pos = plc_client.get_position() + + # Do EXPERT mode execution + while True: + + # 1) SEND REQUEST data to server + # saisie de la requête au clavier et suppression des espaces des 2 côtés + data = input("(EXPERT MODE) REQUEST TO SERVER [ex: ':GD#' (Get Dec), ':GR#' (Get RA)']: ").strip() + # test d'arrêt + if data == "": break + #data_to_send = bytes(data + "\n", "utf-8") + plc_client.send_data(data) + #mysock.sendto("%s" % data, (HOST, PORT)) + #print("Sent: {}".format(data)) + + # 2) RECEIVE REPLY data from server + data_received = plc_client.receive_data() + #reponse, adr = mysock.recvfrom(buf) + #print("Received: {}".format(data_received)) + #print("Useful data received: {}".format(data_useful)) + print('\n') + + #plc_client.close() \ No newline at end of file diff --git a/src/device_controller/concrete_component/device_simulator_common/server_udp_or_tcp.py b/src/device_controller/concrete_component/device_simulator_common/server_udp_or_tcp.py new file mode 100755 index 0000000..8de1565 --- /dev/null +++ b/src/device_controller/concrete_component/device_simulator_common/server_udp_or_tcp.py @@ -0,0 +1,206 @@ +#!/usr/bin/env python3 + +# cf https://docs.python.org/3/library/socketserver.html#socketserver-tcpserver-example + +"""Socket Server implementation + +To be used as a minimalist telescope simulator to which a socket client (SocketClient) can connect +""" + +# Standard library imports +import socketserver + +# Third party imports +# None + +# Local application imports +# None + + +class UnknownCommandException(Exception): + pass + + + + +###STAMP = '01000000' +#STAMP = '0100000000000000' +#HOST, PORT = "localhost", 11110 +#HOST, PORT = "localhost", 9999 +#HOST, PORT = "localhost", 20001 + +# stamp is 8 digits long +STAMP_LENGTH = 8 + +# COMMON CONSTANTS WITH CLIENT +TERMINATOR = '\x00' +COMMAND5 = '050000' +#COMMAND6_SIMPLE = '6' +COMMAND6 = '\x00\x06\x00' + + +class Memo: + + @classmethod + def get(cls, var): + return cls._variables[var] + + @classmethod + def set(cls, var, value): + cls._variables[var] = value + + _variables = { + "C" : '10/01/18', + "L" : '10:20:35', + "g" : '+10', + "t" : '+45:00:00', + } + + +def get_SocketServer_UDP_TCP(myhost:str="localhost", myport:int=11110, PROTOCOL:str="TCP"): + socketserver_type = socketserver.UDPServer if PROTOCOL=="UDP" else socketserver.TCPServer + socketserver_handler_type = socketserver.DatagramRequestHandler if PROTOCOL=="UDP" else socketserver.StreamRequestHandler + + """ + A classic version of request handler: + """ + class _MyUDPorTCPHandler_classic(socketserver.BaseRequestHandler): + # The request handler class for our server. + # It is instantiated once per connection to the server, and must + # override the handle() method to implement communication to the client + + def handle(self): + + # Get received data + # - TCP + if PROTOCOL == "TCP": + # For TCP, self.request is the TCP socket connected to the client + data_rcv = self.request.recv(1024).strip() + # - UDP + else: + # For UDP, self.request consists of a pair of data and client socket + data_rcv = self.request[0].strip() + socket_client = self.request[1] + print("{} wrote:".format(self.client_address[0])) + print(data_rcv) + + # Send back the same data, but upper-cased + # - TCP + if PROTOCOL == "TCP": + self.request.sendall(data_rcv.upper()) + # - UDP + # Since there is no connection, the client address must be given explicitly when sending data back via sendto(). + else: + socket_client.sendto(data_rcv.upper(), self.client_address) + + """ + An alternative request handler class that makes use of streams + (file-like objects that simplify communication by providing the standard file interface): + """ + #class MyTCPHandler(socketserver.StreamRequestHandler): + #class MyUDPHandler(socketserver.DatagramRequestHandler): + class _MyUDPorTCPHandler(socketserver_handler_type): + + + def get_useful_data(self, data_received): + return data_received + + def make_answer_for_request(self, request_bytes:bytes): + #if request == STAMP + ":GD#": return STAMP + "+12:28" + #if request == b'0100000000000000:GD#': return bytes(STAMP + "+12:28", "utf-8") + print("Request received is", request_bytes) + + ''' + 3 types of commands: + - TYPE1: '06' or '050000' + - TYPE2: ':cde#' (mainly ':??#' - i.e : ':GD#', ':GR#', ...) + - TYPE3: 'b?#' (bC# = Cold Start, bW# = selecting Warm Start, bR# = selecting Warm Restart) + ''' + + # Convert to string + request = request_bytes.decode("utf-8") + if len(request) < STAMP_LENGTH+2+1: raise UnknownCommandException(request) + + # Remove TERMINATOR + request = request[0:-1] + + # Remove leading stamp + stamp = request[0:STAMP_LENGTH] + command = request[STAMP_LENGTH:] + print("Command received is", repr(command)) + + # TYPE1 + if command not in (COMMAND5, COMMAND6): + # TYPE2 or 3 + if len(command) < 3: raise UnknownCommandException() + if not (command[-1]=='#'): raise UnknownCommandException() + if not (command[0]==':') and command not in ('bC#','bW#','bR#'): raise UnknownCommandException() + + command_start = command[1:3] + if command == COMMAND6: answer = "G" + elif command == COMMAND5: answer = COMMAND5 + + #gr_request = STAMP + ':GR#' + END + #return bytes(stamp + "15:01:48#" + TERMINATOR, "utf-8") + elif command == ':GR#': answer = "15:01:48" + + #gd_request = STAMP + ':GD#' + END + #if request == bytes(gd_request, "utf-8"): return bytes(STAMP + "+12:28#", "utf-8") + #elif useful_request == 'GD': answer = "+12:28" + elif command == ':GD#': answer = "+12:28" + elif command == ':SG+00#': answer = "1" + elif command == ':GG#': answer = "+00" + elif command_start == 'Gv': answer = 'T' + elif command_start in ('GC','GL', 'Gg', 'Gt'): + answer = Memo.get(command_start[1]) + # Gemini telescope replaces "*" with ":" + if command_start in ('Gg','Gt'): answer = answer.replace('*',':') + else: + # Remove ending '#' + command = command[0:-1] + if command_start in ('SC', 'SL', 'Sg', 'St'): Memo.set(command_start[1],command[3:]) + if command[0] == ':': command = command[1:] + answer = command.upper() + + full_answer_in_bytes = bytes(stamp + answer + '#' + TERMINATOR, "utf-8") + #print("request str upper is", str(request).upper()) + print("Answer sent is", full_answer_in_bytes) + return full_answer_in_bytes + + def handle(self): + + # 1) RECEIVE REQUEST from client + # self.rfile is a file-like object created by the handler; + # we can now use e.g. readline() instead of raw recv() calls + data_received = self.rfile.readline().strip() # data is "bytes" type + data_useful = self.get_useful_data(data_received) + #print("data type is", type(data)) + print("\nFrom {}, received: {}".format(self.client_address[0], data_useful)) + + # 2) SEND REPLY to client + # Likewise, self.wfile is a file-like object used to write back to the client + self.wfile.write(self.make_answer_for_request(data_useful)) + # inutile, ca plante car data est deja en bytes: + #self.wfile.write(data.upper().encode()) + + # Return a "classic" handler: + #return socketserver_type((HOST, PORT), _MyUDPorTCPHandler_classic) + + # or a more "modern" handler: + return socketserver_type((myhost, myport), _MyUDPorTCPHandler) + + + + + + + + +if __name__ == "__main__": + #with socketserver_type((HOST, PORT), MyUDPorTCPHandler_classic) as myserver: + ''' + with socketserver_type((HOST, PORT), MyUDPorTCPHandler) as myserver: + myserver.serve_forever() + ''' + with get_SocketServer_UDP_TCP("localhost", 11110, "UDP") as myserver: + myserver.serve_forever() diff --git a/src/device_controller/concrete_component/gemini/.emptyfile b/src/device_controller/concrete_component/gemini/.emptyfile new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/src/device_controller/concrete_component/gemini/.emptyfile diff --git a/src/device_controller/concrete_component/gemini/client_telescope_gemini_controller_run.py b/src/device_controller/concrete_component/gemini/client_telescope_gemini_controller_run.py new file mode 100755 index 0000000..bc9d2e3 --- /dev/null +++ b/src/device_controller/concrete_component/gemini/client_telescope_gemini_controller_run.py @@ -0,0 +1,270 @@ +#!/usr/bin/env python3 + + +import pprint +import sys + + +''' +(1) +sys.path.append("../../..") +from device_controller.concrete_component.gemini.telescope_controller_gemini import TelescopeControllerGemini +ou (2) +#sys.path.append("..") +#from gemini.telescope_controller_gemini import TelescopeControllerGemini +ou (3) +''' +from gemini_telescope_controller import DeviceControllerTelescopeGemini + +#DEBUG = False +DEBUG = True + + +""" +class essai: + a = 'a' + c = 'c' + def __init__(self): + self.b = 'bB' + def __get__(self, instance, owner): + return self.b + ''' + def __set__(self, instance, value): + self.b = value + def __str__(self): + return self.b + ''' +class user: + e = essai() + +class GenericResult: + ''' Usage: + res = execute(command) + print("result is", res) + if res.ko: raise UnexpectedReturnCode() + if res.ok: + ... + ''' + # By default, bad result + ok = False + ko = True + + def __init__(self, native_result:str, ok=False): + self.txt = native_result + self.ok = ok + self.ko = not ok + ''' + def __str__(self): + return self.native_result + def __repr__(self): + return self.native_result + def __get__(self, instance, owner): + return self.b + def __set__(self, instance, value): + self.b = value + ''' +""" + + + + +def main(): + + # No argument => connexion to REAL TELESCOPE (DISTANT) + if len(sys.argv) == 1: + # Port local AK 8085 = redirigé sur l’IP du tele 192.168.0.12 sur port 11110 + HOST, PORT = "82.64.28.71", 11110 + + # Args given => connexion to LOCAL simulator server + else: + HOST, PORT = "localhost", 11110 + + #tele_ctrl = SocketClient_UDP_TCP(HOST, PORT) + with DeviceControllerTelescopeGemini(HOST, PORT, DEBUG) as tele_ctrl: + + # (optional) Only useful for TCP (does nothing for UDP) + tele_ctrl._connect_to_device() + + #execute_commands_before_init(tele_ctrl) + + # Initialize telescope config (ack, date, time) + #tele_ctrl.do_init() + + #execute_commands_after_init(tele_ctrl) + + #_mes_tests_temporaires_avirer(tele_ctrl) + + # Do MANUAL mode execution + while True: + + tele_ctrl.print_available_commands() + #req = input("\n(EXPERT MODE) REQUEST TO SERVER [ex: '6' (ACK), ':GD#' (Get Dec), ':GR#' (Get RA)'], (ENTER to quit): ").strip() + cmd = input("REQUEST TO SERVER (ENTER to quit): >>> ").strip() + if not cmd: break + + res = tele_ctrl.execute_cmd(cmd) + print("result is", str(res)) + if res.ok: print("OK") + #print("result.txt is", res.txt) + + # END: park telescope + ###tele_ctrl.do_PARK() + + #tele_ctrl.close() + + + +def execute_commands_before_init(tele_ctrl): + print("RA is", tele_ctrl.get_ra()) + + ack = tele_ctrl.get_ack() + print("ack is", ack) + print("ack.txt is", ack) + if ack.ok: print("ack is ok") + if ack.ko: print("ack is ko") + + +def execute_commands_after_init(tele_ctrl): + + #radec = tele_ctrl.get("RA-DEC") + res = tele_ctrl.get_radec() + print("RA-DEC is", res) + + ''' + GOTO and MOVE: + ------------- + ''' + ''' + tele_ctrl.do_goto(ra='21:00:00',dec='+20:00:00') + tele_ctrl.do_goto(ra='22:00:00',dec='+30:00:00') + tele_ctrl.do_move_dir('east','4', 'slew') + ''' + + # In order to execute a Gemini (non generic) command: + res = tele_ctrl.execute_native_cmd(':GR#') + print("res is", res) + + +# @private +def _mes_tests_temporaires_avirer(tele_client): + + print("\n...Execution de mes tests temporaires...\n") + + print("res is", tele_client.run_func('formated_cmd','GD')) + print("dict is") + pprint.pprint(tele_client._cmd) + + ''' + u = user() + print(u.e) + + res = GenericResult('G',False) + print("res is", res) + if res.ok: print("res is ok") + if not res.ko: print("res is not ko") + if res.txt == 'G': print("res is G") + ''' + + tele_client.do_move_dir('EAST','4', 'slew') + tele_client.do_goto(ra='21:00:00',dec='+20:00:00') + ''' + TEST ENVOI DIRECT: + + #data = 0x01000000:06# + data = b'01000000:06#' + + ##data = b'01000000:GD#' + #data = 0x010000003a474423 + #data = b'010000003a474423' + + #data_to_send = tele_client._format_data_to_send(data) + tele_client._send_data(data) + #data_received = tele_client.receive_data() + data_received = tele_client.mysock.recv(1024) + print("data rec = ", data_received) + ''' + + # Send some commands to the Telescope + #pos = tele_client.get_position() + #dec = tele_client.get("DEC") + #ra = tele_client.get("RA") + ''' + tele_client.get("NONE") + tele_client.set("NONE", 1) + tele_client.do("NONE") + ''' + + # TEST BAS NIVEAU POUR ACK 06: + ''' + >>> a=b'\x3A' + >>> a + b':' + >>> a.decode() + ':' + + >>> a=b'\x47' + >>> a + b'G' + >>> a.decode() + 'G' + + >>> a=b'\x44' + >>> a + b'D' + >>> a.decode() + 'D' + + >>> a=b'\x23' + >>> a + b'#' + >>> a.decode() + '#' + >>> tele_client.mysock.sendto(b'\x00\x00\x00\x01\x00\x00\x00\x00\x3A\x47\x44\x23\x00', (HOST, PORT)) + RECEIVED (ALL BYTES): b'\x00\x00\x00\x01\x00\x00\x00\x00+90.000000#\x00' + + >>> a=b'\x52' + >>> a + b'R' + >>> a.decode() + 'R' + >>> tele_client.mysock.sendto(b'\x00\x00\x00\x01\x00\x00\x00\x00\x3A\x47\x52\x23\x00', (HOST, PORT)) + RECEIVED (ALL BYTES): b'\x00\x00\x00\x01\x00\x00\x00\x008.911844#\x00' + + >>> tele_client.mysock.sendto(b'\x00\x00\x00\x01\x00\x00\x00\x00\x00\x06\x00\x00', (HOST, PORT)) + RECEIVED (ALL BYTES): b'\x00\x00\x00\x01\x00\x00\x00\x00G#\x00' + + ''' + #tele_client.mysock.sendto(b'00030000:GD#\x00', (HOST, PORT)) + #tele_client.mysock.sendto(b'00030000\x06\x00', (HOST, PORT)) + #tele_client.mysock.sendto(b'00030000\x00\x06\x00', (HOST, PORT)) + #tele_client.mysock.sendto(b'00030000\0x06\x00', (HOST, PORT)) + #tele_client.mysock.sendto(b'00010000\0x06\x00', (HOST, PORT)) + ''' + # :GD# + ###tele_client.mysock.sendto(b'\x00\x00\x00\x01\x00\x00\x00\x00\x3A\x47\x44\x23\x00', (HOST, PORT)) + + # :GR# + ###tele_client.mysock.sendto(b'\x00\x00\x00\x01\x00\x00\x00\x00\x3A\x47\x52\x23\x00', (HOST, PORT)) + + # ACK 06 OK !!! : + + tele_client.mysock.sendto(b'\x00\x00\x00\x01\x00\x00\x00\x00\x00\x06\x00\x00', (HOST, PORT)) + RECEIVED (ALL BYTES): b'\x00\x00\x00\x01\x00\x00\x00\x00G#\x00' + + tele_client.mysock.sendto(b'00010000\x00\x06\x00\x00', (HOST, PORT)) + RECEIVED (ALL BYTES): b'00010000G#\x00' + ''' + ''' + # wrong: b'00010000\\x00\\x06\\x00\x00' + # ok: + \x05\x00\x00\ + #tele_client.mysock.sendto(b'\x00\x00\x00\x01\x00\x00\x00\x00\x00\x05\x00\x00\x00\x00\x00', (HOST, PORT)) + #tele_client.mysock.sendto(b'00010000\x00\x05\x00\x00', (HOST, PORT)) + tele_client.mysock.sendto(b'00010000\x00\x06\x00\x00', (HOST, PORT)) + data_received = tele_client.receive_data() + ''' + + + +# Execution +main() diff --git a/src/device_controller/concrete_component/gemini/gemini_telescope_controller.py b/src/device_controller/concrete_component/gemini/gemini_telescope_controller.py new file mode 100755 index 0000000..1e6f8fc --- /dev/null +++ b/src/device_controller/concrete_component/gemini/gemini_telescope_controller.py @@ -0,0 +1,286 @@ +#!/usr/bin/env python3 + +"""Socket Client Gemini Telescope implementation +To be used as a concrete class to system control a Gemini telescope +""" + +# Standard library imports +#import socket +#import logging +import sys +import time + +# Third party imports +# None + +# Local application imports +sys.path.append('../../..') +from device_controller.abstract_component.base import UnknownCommandException +#from src.client.socket_client_telescope_abstract import Position, UnknownCommandException, TimeoutException, SocketClientTelescopeAbstract +##from src_socket.client.socket_client_telescope_abstract import * +#from device_controller.abstract_component.telescope_controller_abstract import * +from device_controller.abstract_component.telescope import * + +# Default timeouts +TIMEOUT_SEND = 10 +TIMEOUT_RECEIVE = 10 + +# COMMON CONSTANTS WITH SERVER +TERMINATOR = '\x00' +COMMAND5 = '050000' +#COMMAND6_SIMPLE = '\x00\x06' +COMMAND6 = '\x00\x06\x00' +COMMAND6_SIMPLE = '6' + + + + +##class SocketClientTelescopeGemini(SocketClientTelescopeAbstract): +class DeviceControllerTelescopeGemini(DeviceControllerTelescope): + + # STAMP : + # 00 00 00 00 00 00 00' (8 bytes from '00' to 'FF', 1st one is LOW BYTE) + # ex: 01 + STAMP = 01 00 00 00 00 00 00 00' (8 bytes from '00' to 'FF', 1st one is LOW BYTE) + #STAMP_FILLER = '00000000000000' + #MY_FULL_STAMP = MYSTAMP + STAMP_FILLER + + # Initialize request number (will be increased at each request) + request_num = 0 + # Request number stands on 4 digits (from 0001 to 9999) + request_num_nb_digits = 4 + # For 4 digits, we should get the format '{:04d}': + request_num_format = '{:0'+str(request_num_nb_digits)+'d}' + # Stamp filler with 0 + STAMP_FILLER = '0' * (8 - request_num_nb_digits) + + #data = " ".join(sys.argv[1:]) + #data_to_send = bytes(data + "\n", "utf-8") + + ''' Commands dictionary + NEW foramt is: + 'generic-command-name': ['native-command-name', 'awaited_result_if_ok', 'other-awaited-results if not ok...], + + Old format was: + 'CMD-NAME': ['cmd-get', 'awaited_res_ok_for_get', 'cmd-set', 'awaited_res_ok_for_set', 'comment'], + ''' + # @overwrite + _cmd_device_concrete = { + # GET @ SET commands + 'get_ack': [COMMAND6, 'G', 'B','b','S'], # B# while the initial startup message is being displayed (new in L4), b# while waiting for the selection of the Startup Mode, S# during a Cold Start (new in L4), G# after completed startup. + + # RA-DEC (p109-110) + #:Sr:.# or :Sr::# + #Sets the object's Right Ascension and the object status to "Not Selected". The :Sd# command has to follow to complete the selection. The subsequent use of the :ON...# command is recommended + #:Sd{+-}
{*°}# or :Sd{+- }
{*°:}: + #Sets the object's declination. It is important that the :Sr# command has been send prior. Internal calculations are done that may take up to 0.5 seconds. If the coordinate selection is valid the object status is set to "Selected" + #'RADEC': [('GR','GD'), ''], + #'ra': ['GR', None, 'Sr', None, 'commentaire'], + 'get_ra': ['GR'], + 'set_ra': ['Sr'], + 'get_dec': ['GD'], + 'set_dec': ['Sd'], + # get_radec and set_radec are already defined in abstract class + + 'get_timezone': ['GG'], + 'set_timezone': ['SG', '1'], + + # ALT-AZ (p?) + "get_alt": ['GA'], + "get_az": ['GZ'], + #"ALT-AZ": [('GA','GZ'), ''], + + # LONG-LAT + "get_long": ['Gg'], + "set_long": ['Sg'], + "get_lat": ['Gt'], + "set_lat": ['St'], + #"LONGLAT": [('Gg','Gt'), ''], + + "get_hangle": ['GH'], + + 'get_vel': ['Gv'], + #"get_maxvel": ['Gv'], + + 'get_date': ['GC'], + 'set_date': ['SC'], + 'get_time': ['GL'], + 'set_time': ['SL'], + + # DO commands + # defined in abstract class: + #'do_init': ['do_init'], + 'do_park': ['hP'], + 'do_warm_start': ['bW'], + 'do_prec_refr': ['p3'], + 'do_move': ['MS', '0', '1Object below horizon.', '2No object selected.', '3Manual Control.', '4Position unreachable.', '5Not aligned.', '6Outside Limits.' ], + 'do_movenorth': ['Mn'], + 'do_movesouth': ['Ms'], + 'do_movewest': ['Mw'], + 'do_moveeast': ['Me'], + 'do_stop': ['Q'], + } + + + # @overwrite + # Gemini is using UDP + def __init__(self, device_host:str="localhost", device_port:int=11110, DEBUG=False): + super().__init__(device_host, device_port, "SOCKET-UDP", 1024, DEBUG) + + + # @overwrite + def formated_cmd(self, cmd:str, values_to_set:str=None)->str: + if values_to_set != None: + for value_to_set in values_to_set: + cmd += value_to_set + if cmd not in (COMMAND6, COMMAND5): + cmd += '#' + if cmd not in ('bC#','bW#','bR#'): + cmd=':'+cmd + return cmd + + + + #@override + def encapsulate_data_to_send(self, command:str): + r''' Encapsulate useful data to be ready for sending + + If data is "complete" (with stamp, and well formatted), send it as is + Otherwise, add stamp and format it + + 3 types of commands: + - TYPE1: '06' or '050000' + - TYPE2: ':cde#' (mainly ':??#' - i.e : ':GD#', ':GR#', ...) + - TYPE3: 'b?#' (bC# = Cold Start, bW# = selecting Warm Start, bR# = selecting Warm Restart) + + :Examples: + >>> tele = TelescopeControllerGemini("localhost", 11110, DEBUG=False) + >>> tele.encapsulate_data_to_send(':GD#') + '00010000:GD#\x00' + >>> tele.encapsulate_data_to_send(':GR#') + '00020000:GR#\x00' + >>> tele.close() + + # ne marche pas => '00010000:GD#\x00' + ''' + + ####return bytes(self.MYSTAMP + self.STAMP_FILLER + data + "\n", "utf-8") + + # TYPE1 + #print("command to encapsulate is", repr(command)) + + if command == COMMAND6_SIMPLE: command = COMMAND6 + + if command not in (COMMAND6, COMMAND5): + # TYPE2 or 3 + #has_starting_car = data.find(':') > -1 + if len(command) < 3: raise UnknownCommandException() + if not (command[-1]=='#'): raise UnknownCommandException() + if not (command[0]==':') and command not in ('bC#','bW#','bR#'): raise UnknownCommandException() + #stamp,command = data.split(':',1) + + #if command.find('#')>-1: command,_ = command.split('#',1) + self.request_num += 1 + # Format to request_num_nb_digits (4) digits + request_num_str = self.request_num_format.format(self.request_num) + self.last_stamp = request_num_str + self.STAMP_FILLER + + #if command == COMMAND6_SIMPLE: command = COMMAND6_REAL + data_encapsulated = self.last_stamp + command + TERMINATOR + #return super().encapsulate_data_to_send(data_encapsulated) + #return self.my_channel.encapsulate_data_to_send(data_encapsulated) + return data_encapsulated + + #return bytes(data + "\n", "utf-8") + ####return bytes(self.MYSTAMP + self.STAMP_FILLER + data + "\n", "utf-8") + + # @overwrite + def uncap_received_data(self, data_received:str)->str: + #data_received = super().uncap_received_data(data_received_bytes) + ##data_received = self.my_channel.uncap_received_data(data_received_bytes) + + #>>> tele.uncap_received_data(b'001700001\x00') + + r""" + Extract useful data from received raw data + + >>> tele = TelescopeControllerGemini("localhost", 11110, DEBUG=False) + >>> tele.last_stamp = '00170000' + >>> tele.uncap_received_data('001700001#') + '1' + >>> tele.close() + """ + + print("data_received_bytes type is", type(data_received)) + print("data received is", data_received) + ##data_received = data_received_bytes.decode() + #print("data_received is", data_received) + # Remove STAMP (and \n at the end): + #useful_data = data_received.split(self.MY_FULL_STAMP)[1][:-1] + useful_data = data_received.split(self.last_stamp)[1][:-1] + # Remove '#' at the end, if exists + if useful_data[-1] == '#': useful_data = useful_data[:-1] + return useful_data + + + # @overwrite + #def get_ack(self): return self.execute_native_cmd(COMMAND6, 'G') + #def do_warm_start(self): return self.execute_native_cmd('bW') + + #TODO: déplacer dans parent avec nouvelles commandes set_speed_slew, set_speed_average, ... + # @override + def set_speed(self, speed_rate): + native_cmd = None + # quick + if speed_rate == "slew": native_cmd = 'RS' + # average + if speed_rate == "average": native_cmd = 'RM' + # slow + if speed_rate == "center": native_cmd = 'RC' + # very slow + if speed_rate == "guide": native_cmd = 'RG' + if not native_cmd: raise UnknownCommandException(speed_rate) + return self.execute_unformated_native_cmd(native_cmd) + + +if __name__ == "__main__": + + import doctest + doctest.testmod() + exit() + """ + # Classic usage: + #tele_ctrl = SocketClient_Gemini(HOST, PORT) + # More elegant usage, using "with": + ##with SocketClientTelescopeGemini("localhost", 11110, DEBUG=False) as tele_ctrl: + with TelescopeControllerGemini("localhost", 11110, DEBUG=False) as tele_ctrl: + + # 0) CONNECT to server (only for TCP, does nothing for UDP) + tele_ctrl._connect_to_server() + + #tele_ctrl.config() + + # Send some commands to the Telescope + #pos = tele_ctrl.get_position() + + # Do EXPERT mode execution + while True: + + # 1) SEND REQUEST data to server + # saisie de la requête au clavier et suppression des espaces des 2 côtés + data = input("(EXPERT MODE) REQUEST TO SERVER [ex: ':GD#' (Get Dec), ':GR#' (Get RA)']: ").strip() + # test d'arrêt + if data == "": break + #data_to_send = bytes(data + "\n", "utf-8") + tele_ctrl.send_data(data) + #mysock.sendto("%s" % data, (HOST, PORT)) + #print("Sent: {}".format(data)) + + # 2) RECEIVE REPLY data from server + data_received = tele_ctrl.receive_data() + #reponse, adr = mysock.recvfrom(buf) + #print("Received: {}".format(data_received)) + #print("Useful data received: {}".format(data_useful)) + print('\n') + + #tele_ctrl.close() + """ \ No newline at end of file diff --git a/src/device_controller/concrete_component/gemini/gemini_telescope_simulator.py b/src/device_controller/concrete_component/gemini/gemini_telescope_simulator.py new file mode 100755 index 0000000..825dd3d --- /dev/null +++ b/src/device_controller/concrete_component/gemini/gemini_telescope_simulator.py @@ -0,0 +1,40 @@ +#!/usr/bin/env python3 + +import sys + + +#sys.path.append("..") +#sys.path.append("../..") +#sys.path.append("../../..") +#from device_controller.concrete_component.device_simulator_common.server_udp_or_tcp import get_SocketServer_UDP_TCP +#from device_simulator_common.server_udp_or_tcp import get_SocketServer_UDP_TCP +from device_controller.concrete_component.device_simulator_common.server_udp_or_tcp import get_SocketServer_UDP_TCP +#from server.server_udp_or_tcp import get_SocketServer_UDP_TCP + +HOST = "localhost" + + +# Voir https://stackoverflow.com/questions/10085996/shutdown-socketserver-serve-forever-in-one-thread-python-application + +#class TelescopeGeminiSimulator: +class DeviceSimulatorTelescopeGemini: + #with socketserver_type((HOST, PORT), MyUDPorTCPHandler_classic) as myserver: + + myserver = None + + @classmethod + def serve_forever(cls, PORT): + #with get_SocketServer_UDP_TCP(HOST, PORT, "UDP") as myserver: myserver.serve_forever() + cls.myserver = get_SocketServer_UDP_TCP(HOST, PORT, "UDP") + print("******** myserver: *********", cls.myserver) + try: + cls.myserver.serve_forever() + except KeyboardInterrupt: + pass + cls.myserver.server_close() + cls.myserver.shutdown() + + @classmethod + def stop(cls): + print("******** myserver: *********", cls.myserver) + cls.myserver.shutdown() diff --git a/src/device_controller/concrete_component/gemini/server_telescope_gemini_simulator_run.py b/src/device_controller/concrete_component/gemini/server_telescope_gemini_simulator_run.py new file mode 100755 index 0000000..8521d3f --- /dev/null +++ b/src/device_controller/concrete_component/gemini/server_telescope_gemini_simulator_run.py @@ -0,0 +1,18 @@ +#!/usr/bin/env python3 + +import sys + + +sys.path.append("..") +#from device_controller.concrete_component.device_simulator_common.server_udp_or_tcp import get_SocketServer_UDP_TCP +from device_simulator_common.server_udp_or_tcp import get_SocketServer_UDP_TCP +#from server.server_udp_or_tcp import get_SocketServer_UDP_TCP + +HOST, PORT = "localhost", 11110 +#HOST, PORT = "localhost", 9999 +#HOST, PORT = "localhost", 20001 + +if __name__ == "__main__": + #with socketserver_type((HOST, PORT), MyUDPorTCPHandler_classic) as myserver: + with get_SocketServer_UDP_TCP(HOST, PORT, "UDP") as myserver: + myserver.serve_forever() diff --git a/src/device_controller/concrete_component/sbig/sbig_controller.py b/src/device_controller/concrete_component/sbig/sbig_controller.py new file mode 100755 index 0000000..14031b5 --- /dev/null +++ b/src/device_controller/concrete_component/sbig/sbig_controller.py @@ -0,0 +1,299 @@ +#!/usr/bin/env python3 + +"""Socket Client Gemini Telescope implementation +To be used as a concrete class to system control a Gemini telescope +""" + +# Standard library imports +#import socket +#import logging +import sys +import time + +# Third party imports +# None + + +# Local application imports + +# device_controller/ +sys.path.append('../../..') + +# src/ +sys.path.append('../../../..') + +from device_controller.abstract_component.base import UnknownCommandException +#from device_controller.abstract_component.telescope_controller_abstract import * + +#TODO: Heritage ou plutot COMPOSITION ? +# The SBIG controller has 3 capabilities : filter selector, detector sensor, and detector shutter +#from device_controller.abstract_component.base import * +from device_controller.abstract_component.filter_selector import DeviceControllerFilterSelector +from device_controller.abstract_component.detector_sensor import DeviceControllerDetectorSensor +from device_controller.abstract_component.detector_shutter import DeviceControllerDetectorShutter + +# Default timeouts +TIMEOUT_SEND = 10 +TIMEOUT_RECEIVE = 10 + +# COMMON CONSTANTS WITH SERVER +TERMINATOR = '\x00' +COMMAND5 = '050000' +#COMMAND6_SIMPLE = '\x00\x06' +COMMAND6 = '\x00\x06\x00' +COMMAND6_SIMPLE = '6' + + + + +##class SocketClientTelescopeGemini(SocketClientTelescopeAbstract): +#class TelescopeControllerGemini(TelescopeControllerAbstract): +#class DeviceControllerSBIG(DeviceControllerAbstract): +class DeviceControllerSBIG(DeviceControllerDetectorSensor, DeviceControllerDetectorShutter, DeviceControllerFilterSelector): + + # STAMP : + # 00 00 00 00 00 00 00' (8 bytes from '00' to 'FF', 1st one is LOW BYTE) + # ex: 01 + STAMP = 01 00 00 00 00 00 00 00' (8 bytes from '00' to 'FF', 1st one is LOW BYTE) + #STAMP_FILLER = '00000000000000' + #MY_FULL_STAMP = MYSTAMP + STAMP_FILLER + + # Initialize request number (will be increased at each request) + request_num = 0 + # Request number stands on 4 digits (from 0001 to 9999) + request_num_nb_digits = 4 + # For 4 digits, we should get the format '{:04d}': + request_num_format = '{:0'+str(request_num_nb_digits)+'d}' + # Stamp filler with 0 + STAMP_FILLER = '0' * (8 - request_num_nb_digits) + + #data = " ".join(sys.argv[1:]) + #data_to_send = bytes(data + "\n", "utf-8") + + ''' Commands dictionary + NEW foramt is: + 'generic-command-name': ['native-command-name', 'awaited_result_if_ok', 'other-awaited-results if not ok...], + + Old format was: + 'CMD-NAME': ['cmd-get', 'awaited_res_ok_for_get', 'cmd-set', 'awaited_res_ok_for_set', 'comment'], + ''' + # @overwrite + _cmd_device_concrete = { + # GET @ SET commands + 'get_ack': [COMMAND6, 'G', 'B','b','S'], # B# while the initial startup message is being displayed (new in L4), b# while waiting for the selection of the Startup Mode, S# during a Cold Start (new in L4), G# after completed startup. + + # RA-DEC (p109-110) + #:Sr:.# or :Sr::# + #Sets the object's Right Ascension and the object status to "Not Selected". The :Sd# command has to follow to complete the selection. The subsequent use of the :ON...# command is recommended + #:Sd{+-}
{*°}# or :Sd{+- }
{*°:}: + #Sets the object's declination. It is important that the :Sr# command has been send prior. Internal calculations are done that may take up to 0.5 seconds. If the coordinate selection is valid the object status is set to "Selected" + #'RADEC': [('GR','GD'), ''], + #'ra': ['GR', None, 'Sr', None, 'commentaire'], + 'get_ra': ['GR'], + 'set_ra': ['Sr'], + 'get_dec': ['GD'], + 'set_dec': ['Sd'], + # get_radec and set_radec are already defined in abstract class + + 'get_timezone': ['GG'], + 'set_timezone': ['SG', '1'], + + # ALT-AZ (p?) + "get_alt": ['GA'], + "get_az": ['GZ'], + #"ALT-AZ": [('GA','GZ'), ''], + + # LONG-LAT + "get_long": ['Gg'], + "set_long": ['Sg'], + "get_lat": ['Gt'], + "set_lat": ['St'], + #"LONGLAT": [('Gg','Gt'), ''], + + "get_hangle": ['GH'], + + 'get_vel': ['Gv'], + #"get_maxvel": ['Gv'], + + 'get_date': ['GC'], + 'set_date': ['SC'], + 'get_time': ['GL'], + 'set_time': ['SL'], + + # DO commands + # defined in abstract class: + #'do_init': ['do_init'], + 'do_park': ['hP'], + 'do_warm_start': ['bW'], + 'do_prec_refr': ['p3'], + 'do_move': ['MS', '0', '1Object below horizon.', '2No object selected.', '3Manual Control.', '4Position unreachable.', '5Not aligned.', '6Outside Limits.' ], + 'do_movenorth': ['Mn'], + 'do_movesouth': ['Ms'], + 'do_movewest': ['Mw'], + 'do_moveeast': ['Me'], + 'do_stop': ['Q'], + } + + + # @overwrite + # Gemini is using UDP + def __init__(self, device_host:str="localhost", device_port:int=11110, DEBUG=False): + super().__init__(device_host, device_port, "SOCKET-UDP", 1024, DEBUG) + + + # @overwrite + def formated_cmd(self, cmd:str, values_to_set:str=None)->str: + if values_to_set != None: + for value_to_set in values_to_set: + cmd += value_to_set + if cmd not in (COMMAND6, COMMAND5): + cmd += '#' + if cmd not in ('bC#','bW#','bR#'): + cmd=':'+cmd + return cmd + + + + #@override + def encapsulate_data_to_send(self, command:str): + r''' Encapsulate useful data to be ready for sending + + If data is "complete" (with stamp, and well formatted), send it as is + Otherwise, add stamp and format it + + 3 types of commands: + - TYPE1: '06' or '050000' + - TYPE2: ':cde#' (mainly ':??#' - i.e : ':GD#', ':GR#', ...) + - TYPE3: 'b?#' (bC# = Cold Start, bW# = selecting Warm Start, bR# = selecting Warm Restart) + + :Examples: + >>> tele = TelescopeControllerGemini("localhost", 11110, DEBUG=False) + >>> tele.encapsulate_data_to_send(':GD#') + '00010000:GD#\x00' + >>> tele.encapsulate_data_to_send(':GR#') + '00020000:GR#\x00' + >>> tele.close() + + # ne marche pas => '00010000:GD#\x00' + ''' + + ####return bytes(self.MYSTAMP + self.STAMP_FILLER + data + "\n", "utf-8") + + # TYPE1 + #print("command to encapsulate is", repr(command)) + + if command == COMMAND6_SIMPLE: command = COMMAND6 + + if command not in (COMMAND6, COMMAND5): + # TYPE2 or 3 + #has_starting_car = data.find(':') > -1 + if len(command) < 3: raise UnknownCommandException() + if not (command[-1]=='#'): raise UnknownCommandException() + if not (command[0]==':') and command not in ('bC#','bW#','bR#'): raise UnknownCommandException() + #stamp,command = data.split(':',1) + + #if command.find('#')>-1: command,_ = command.split('#',1) + self.request_num += 1 + # Format to request_num_nb_digits (4) digits + request_num_str = self.request_num_format.format(self.request_num) + self.last_stamp = request_num_str + self.STAMP_FILLER + + #if command == COMMAND6_SIMPLE: command = COMMAND6_REAL + data_encapsulated = self.last_stamp + command + TERMINATOR + #return super().encapsulate_data_to_send(data_encapsulated) + #return self.my_channel.encapsulate_data_to_send(data_encapsulated) + return data_encapsulated + + #return bytes(data + "\n", "utf-8") + ####return bytes(self.MYSTAMP + self.STAMP_FILLER + data + "\n", "utf-8") + + # @overwrite + def uncap_received_data(self, data_received:str)->str: + #data_received = super().uncap_received_data(data_received_bytes) + ##data_received = self.my_channel.uncap_received_data(data_received_bytes) + + #>>> tele.uncap_received_data(b'001700001\x00') + + r""" + Extract useful data from received raw data + + >>> tele = TelescopeControllerGemini("localhost", 11110, DEBUG=False) + >>> tele.last_stamp = '00170000' + >>> tele.uncap_received_data('001700001#') + '1' + >>> tele.close() + """ + + print("data_received_bytes type is", type(data_received)) + print("data received is", data_received) + ##data_received = data_received_bytes.decode() + #print("data_received is", data_received) + # Remove STAMP (and \n at the end): + #useful_data = data_received.split(self.MY_FULL_STAMP)[1][:-1] + useful_data = data_received.split(self.last_stamp)[1][:-1] + # Remove '#' at the end, if exists + if useful_data[-1] == '#': useful_data = useful_data[:-1] + return useful_data + + + # @overwrite + #def get_ack(self): return self.execute_native_cmd(COMMAND6, 'G') + #def do_warm_start(self): return self.execute_native_cmd('bW') + + #TODO: déplacer dans parent avec nouvelles commandes set_speed_slew, set_speed_average, ... + # @override + def set_speed(self, speed_rate): + native_cmd = None + # quick + if speed_rate == "slew": native_cmd = 'RS' + # average + if speed_rate == "average": native_cmd = 'RM' + # slow + if speed_rate == "center": native_cmd = 'RC' + # very slow + if speed_rate == "guide": native_cmd = 'RG' + if not native_cmd: raise UnknownCommandException(speed_rate) + return self.execute_unformated_native_cmd(native_cmd) + + +if __name__ == "__main__": + + import doctest + doctest.testmod() + exit() + """ + # Classic usage: + #tele_ctrl = SocketClient_Gemini(HOST, PORT) + # More elegant usage, using "with": + ##with SocketClientTelescopeGemini("localhost", 11110, DEBUG=False) as tele_ctrl: + with TelescopeControllerGemini("localhost", 11110, DEBUG=False) as tele_ctrl: + + # 0) CONNECT to server (only for TCP, does nothing for UDP) + tele_ctrl._connect_to_server() + + #tele_ctrl.config() + + # Send some commands to the Telescope + #pos = tele_ctrl.get_position() + + # Do EXPERT mode execution + while True: + + # 1) SEND REQUEST data to server + # saisie de la requête au clavier et suppression des espaces des 2 côtés + data = input("(EXPERT MODE) REQUEST TO SERVER [ex: ':GD#' (Get Dec), ':GR#' (Get RA)']: ").strip() + # test d'arrêt + if data == "": break + #data_to_send = bytes(data + "\n", "utf-8") + tele_ctrl.send_data(data) + #mysock.sendto("%s" % data, (HOST, PORT)) + #print("Sent: {}".format(data)) + + # 2) RECEIVE REPLY data from server + data_received = tele_ctrl.receive_data() + #reponse, adr = mysock.recvfrom(buf) + #print("Received: {}".format(data_received)) + #print("Useful data received: {}".format(data_useful)) + print('\n') + + #tele_ctrl.close() + """ \ No newline at end of file diff --git a/src/device_controller/concrete_component/sbig/sbig_simulator.py b/src/device_controller/concrete_component/sbig/sbig_simulator.py new file mode 100755 index 0000000..425e809 --- /dev/null +++ b/src/device_controller/concrete_component/sbig/sbig_simulator.py @@ -0,0 +1,39 @@ +#!/usr/bin/env python3 + +import sys + + +#sys.path.append("..") +#sys.path.append("../..") +#sys.path.append("../../..") +#from device_controller.concrete_component.device_simulator_common.server_udp_or_tcp import get_SocketServer_UDP_TCP +#from device_simulator_common.server_udp_or_tcp import get_SocketServer_UDP_TCP +from device_controller.concrete_component.device_simulator_common.server_udp_or_tcp import get_SocketServer_UDP_TCP +#from server.server_udp_or_tcp import get_SocketServer_UDP_TCP + +HOST = "localhost" + + +# Voir https://stackoverflow.com/questions/10085996/shutdown-socketserver-serve-forever-in-one-thread-python-application + +class DeviceSimulatorSBIG: + #with socketserver_type((HOST, PORT), MyUDPorTCPHandler_classic) as myserver: + + myserver = None + + @classmethod + def serve_forever(cls, PORT): + #with get_SocketServer_UDP_TCP(HOST, PORT, "UDP") as myserver: myserver.serve_forever() + cls.myserver = get_SocketServer_UDP_TCP(HOST, PORT, "UDP") + print("******** myserver: *********", cls.myserver) + try: + cls.myserver.serve_forever() + except KeyboardInterrupt: + pass + cls.myserver.server_close() + cls.myserver.shutdown() + + @classmethod + def stop(cls): + print("******** myserver: *********", cls.myserver) + cls.myserver.shutdown() diff --git a/src/device_controller/doc/Device_controller_class_diag.pu b/src/device_controller/doc/Device_controller_class_diag.pu new file mode 100755 index 0000000..2e4bfa2 --- /dev/null +++ b/src/device_controller/doc/Device_controller_class_diag.pu @@ -0,0 +1,153 @@ + +@startuml + +/' +UML class Diagram : can be displayed with PlantUML (plugin for Eclipse or for PyCharm) + +PlantUML: +- How to install : https://projects.irap.omp.eu/projects/pyros/wiki/Project_Development#PlantUML +- Eclipse plugin : http://plantuml.com/eclipse +- class diagrams : http://plantuml.com/class-diagram +- sequence diagrams : http://plantuml.com/sequence-diagram +- state diagrams : http://plantuml.com/state-diagram +- Use Case diagrams : http://plantuml.com/use-case-diagram +- OLD Activity diagrams : http://plantuml.com/activity-diagram-legacy +- NEW Activity diagrams : http://plantuml.com/activity-diagram-beta +- Pre-processing (include...) : http://plantuml.com/preprocessing +- GANTT diagrams : http://plantuml.com/gantt-diagram +- REAL WORLD EXAMPLES !!! : https://real-world-plantuml.com/ +- For Python: + - https://github.com/SamuelMarks/python-plantuml + - https://pythonhosted.org/plantuml/ + +UML diagrams theory : https://www.ibm.com/developerworks/rational/library/content/RationalEdge/sep04/bell/index.html +'/ + +title +__**DeviceController and ClientChannel classes diagram (composition)**__ +(ClientChannel is a COMPONENT of DeviceController) +end title + + +/' Channels '/ +ClientChannel <|-- ClientSerial +ClientChannel <|-- ClientSocket +ClientChannel <|-- ClientUSB + +abstract class ClientChannel { + my_channel # socket or serial or usb... + {abstract} connect_to_server() + {abstract} read() + {abstract} put() + put_read() + {abstract} send_data() + {abstract} receive_data() + {abstract} close() +} + +class ClientSocket { + my_channel # (socket) + -- + connect_to_server() + send_data() + receive_data() + close() +} + + +/' Abstract Devices Controllers '/ +DeviceControllerAbstract o-- ClientChannel + +DeviceControllerAbstract <|-- MountPointingControllerAbstract +DeviceControllerAbstract <|-- SecurityManagementControllerAbstract +DeviceControllerAbstract <|-- DetectorSensorControllerAbstract +DeviceControllerAbstract <|-- DetectorShutterControllerAbstract +DeviceControllerAbstract <|-- DetectorFocusControllerAbstract +DeviceControllerAbstract <|-- FilterSelectorControllerAbstract + +abstract class DeviceControllerAbstract { + my_channel # socket or serial or usb... + _cmd = {start, stop, park...} + class GenericResult + -- + {abstract} connect_to_device() + {abstract} _format_data_to_send() + {abstract} _unformat_received_data() + available_commands() + execute() + execute_generic_cmd() + execute_native_cmd() + execute_native_cmd() + send_data() + receive_data() + --- + **Abstract GET/SET/DO commands :** + {abstract} get_timezone(), set_timezone() + {abstract} get_date(), set_date() + {abstract} get_time(), set_time() + {abstract} do_park() + {abstract} do_start() + {abstract} do_stop() + {abstract} do_init() +} + +abstract class MountPointingControllerAbstract { + _cmd = {get_ra, get_dec, do_goto...} + ---- + **Abstract GET/SET/DO commands :** + {abstract} get_ack() + {abstract} get_ra(), set_ra() + {abstract} get_dec(), set_dec() + get_radec(), set_radec() + {abstract} get_lat(), set_lat() + {abstract} get_long(), set_long() + {abstract} set_speed() + {abstract} do_warm_start() + {abstract} do_prec_refr() + ---- + **Generic MACRO commands:** + do_init() + do_goto() + do_move() +} + +abstract class SecurityManagementControllerAbstract { + _cmd = {get_status, set_light...} +} + +abstract class DetectorSensorControllerAbstract { + _cmd = {set_pose, do_start_acq...} +} + +/' Concrete Devices Controllers '/ + +MountPointingControllerAbstract <|-- TelescopeControllerMeade +MountPointingControllerAbstract <|-- TelescopeControllerGemini +MountPointingControllerAbstract <|-- TelescopeControllerColibri + +SecurityManagementControllerAbstract <|-- PLCControllerAK +SecurityManagementControllerAbstract <|-- PLCControllerColibri + +DetectorSensorControllerAbstract <|-- CameraControllerCAGIRE +DetectorSensorControllerAbstract <|-- CameraControllerDDRAGO + +DetectorSensorControllerAbstract <|-- SBIGController +DetectorShutterControllerAbstract <|-- SBIGController +FilterSelectorControllerAbstract <|-- SBIGController + +SBIGController o-> SBIGSimulator +SBIGController <-- AgentDeviceSBIG + +MountPointingControllerAbstract <|-- DeltaTauController +DetectorFocusControllerAbstract <|-- DeltaTauController +FilterSelectorControllerAbstract <|-- DeltaTauController + +class TelescopeControllerGemini { + _cmd = {get_ra, get_dec, do_goto...} + format_data_to_send() + unformat_received_data() +} + +TelescopeControllerMeade : _cmd = {get_ra, get_dec, do_goto...} + +@enduml diff --git a/src/device_controller/doc/Device_controller_class_diag_multiinheritance_OLD.pu b/src/device_controller/doc/Device_controller_class_diag_multiinheritance_OLD.pu new file mode 100755 index 0000000..2cbf361 --- /dev/null +++ b/src/device_controller/doc/Device_controller_class_diag_multiinheritance_OLD.pu @@ -0,0 +1,58 @@ + +/' +UML class Diagram : can be displayed with PlantUML (plugin for Eclipse or for PyCharm) + +PlantUML: +- How to install : https://projects.irap.omp.eu/projects/pyros/wiki/Project_Development#PlantUML +- Eclipse plugin : http://plantuml.com/eclipse +- class diagrams : http://plantuml.com/class-diagram +- sequence diagrams : http://plantuml.com/sequence-diagram +- state diagrams : http://plantuml.com/state-diagram +- Use Case diagrams : http://plantuml.com/use-case-diagram +- OLD Activity diagrams : http://plantuml.com/activity-diagram-legacy +- NEW Activity diagrams : http://plantuml.com/activity-diagram-beta +- Pre-processing (include...) : http://plantuml.com/preprocessing +- GANTT diagrams : http://plantuml.com/gantt-diagram +- REAL WORLD EXAMPLES !!! : https://real-world-plantuml.com/ +- For Python: + - https://github.com/SamuelMarks/python-plantuml + - https://pythonhosted.org/plantuml/ + +UML diagrams theory : https://www.ibm.com/developerworks/rational/library/content/RationalEdge/sep04/bell/index.html +'/ + + +@startuml + +title +__**DeviceController and ClientChannel classes diagram (multi-inheritance)**__ +(TelescopeGemini heritates both from DeviceController and ClientChannel) + +end title + + +/' Abstract Devices Controllers '/ +DeviceControllerAbstract <|-- PLCControllerAbstract +DeviceControllerAbstract <|-- CameraControllerAbstract +DeviceControllerAbstract <|-- TelescopeControllerAbstract + +/' Concrete Devices Controllers '/ + +TelescopeControllerAbstract <|-- TelescopeControllerMeade +TelescopeControllerAbstract <|-- TelescopeControllerGemini +ClientSocket <|-- TelescopeControllerGemini +TelescopeControllerAbstract <|-- TelescopeControllerColibri + +PLCControllerAbstract <|-- PLCControllerAK +PLCControllerAbstract <|-- PLCControllerColibri + +CameraControllerAbstract <|-- CameraControllerVIS_AK +CameraControllerAbstract <|-- CameraControllerCAGIRE +CameraControllerAbstract <|-- CameraControllerDDRAGO + +/' Channels '/ +ClientChannel <|-- ClientSocket +ClientChannel <|-- ClientSerial +ClientChannel <|-- ClientUSB + +@enduml diff --git a/src/device_controller/doc/generate_diagrams.sh b/src/device_controller/doc/generate_diagrams.sh new file mode 100755 index 0000000..0ae7c7a --- /dev/null +++ b/src/device_controller/doc/generate_diagrams.sh @@ -0,0 +1,5 @@ +# pip install plantuml +# https://github.com/SamuelMarks/python-plantuml +# https://pythonhosted.org/plantuml/ + +python3 -m plantuml *.pu diff --git a/src/device_controller/logs.py b/src/device_controller/logs.py new file mode 100755 index 0000000..efdd126 --- /dev/null +++ b/src/device_controller/logs.py @@ -0,0 +1,77 @@ +#!/usr/bin/env python3 + +"""LOGGER + +""" + + +# Standard library imports +import logging + +# Third party imports +# None + +# Local application imports +# None + + + + +logger = None +# Aliases for logger: +def log_d(msg:str): logger.debug(msg) +def log_i(msg:str): logger.info(msg) +def log_w(msg:str): logger.warning(msg) +def log_e(msg:str): logger.error(msg) +def log_c(msg:str): logger.critical(msg) + + + +#def set_logger(self): +def set_logger(DEBUG=False): + global logger + ''' + # Logger configuration + # Log all events, starting from DEBUG level + ''' + + # Basic configuration + ''' + #logging.basicConfig(level=logging.DEBUG) + logging.basicConfig(level=logging.DEBUG, filename='client.log', filemode='a', format='%(asctime)s - %(name)s - %(levelname)s - %(message)s') + logging.debug("Client instanciated") + ''' + + # Advanced configuration + logger = logging.getLogger(__name__) + # Absolument necessaire, sinon rien ne marche !!! + logger.setLevel(logging.DEBUG) + + # Create handlers for writing to console and file + c_handler = logging.StreamHandler() + f_handler = logging.FileHandler('client.log') + + # Set level for each handler + # DEBUG < INFO < WARNING < ERROR < CRITICAL + #if self.DEBUG: + if DEBUG: + c_handler.setLevel(logging.DEBUG) + f_handler.setLevel(logging.DEBUG) + else: + c_handler.setLevel(logging.INFO) + f_handler.setLevel(logging.INFO) + + # Set format for each handler + #c_format = logging.Formatter('%(name)s - %(levelname)s - %(message)s') + c_format = logging.Formatter('%(message)s') + f_format = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') + c_handler.setFormatter(c_format) + f_handler.setFormatter(f_format) + + # Set handlers (for console and file) + logger.addHandler(c_handler) + logger.addHandler(f_handler) + + #return logger + + diff --git a/src/device_controller/test/.gitignore b/src/device_controller/test/.gitignore new file mode 100644 index 0000000..e78b574 --- /dev/null +++ b/src/device_controller/test/.gitignore @@ -0,0 +1 @@ +/client.log diff --git a/src/device_controller/test/test_client_gemini.py b/src/device_controller/test/test_client_gemini.py new file mode 100755 index 0000000..d9680b2 --- /dev/null +++ b/src/device_controller/test/test_client_gemini.py @@ -0,0 +1,78 @@ +#!/usr/bin/env python3 + +import threading + +import unittest + +import sys +sys.path.append('..') +sys.path.append('../..') +from device_controller.concrete_component.device_simulator_common.server_udp_or_tcp import get_SocketServer_UDP_TCP +#from src_socket.client.socket_client_telescope_gemini import SocketClientTelescopeGemini +import device_controller.concrete_component.gemini.gemini_telescope_controller as gemini + +#HOST, PORT = "localhost", 9999 +#HOST, PORT = "localhost", 20001 +HOST, PORT = "localhost", 11110 + +class TestClient(unittest.TestCase): + + + def setUp(self): + self.seq = range(10) + + def server(self): + with get_SocketServer_UDP_TCP(HOST, PORT, "UDP") as myserver: + myserver.serve_forever() + + + def test_run_doctests(self): + import doctest + doctest.testmod(gemini) + + + def test_run_unittests(self): + + q_a = [ + (':toto#', 'TOTO'), + (':GD#', '+12:28'), + #(':GD#', '+12:28') + ('6', 'G'), + ] + + #TODO: RUN SERVER in a thread + t_server = threading.Thread(target=self.server) + #threads.append(t) + t_server.start() + #time.sleep(3) + + # RUN CLIENT to connect to server + #tele_client = SocketClient_UDP_TCP(HOST, PORT, "UDP") + with gemini.DeviceControllerTelescopeGemini(HOST, PORT) as tele_client: + # Only useful for TCP (does nothing for UDP) + tele_client._connect_to_device() + + # 1) SEND REQUEST data to server + + for data in q_a: + question,answer = data + tele_client.send_data(question) + + # 2) RECEIVE REPLY data from server + data_received = tele_client.receive_data() + self.assertEqual(data_received, answer) + + radec = tele_client.get_radec() + print("ra-dec is", radec) + #self.assertEqual(radec, ('15:01:48', '+12:28')) + self.assertEqual(radec.txt, '15:01:48,+12:28') + + #tele_client.close() + + #TODO: Stop the server thread (t_server), how can we do that ? + # cf https://www.oreilly.com/library/view/python-cookbook-2nd/0596007973/ch09s03.html + #t_server.suicide_toi() !!! + + +if __name__ == '__main__': + unittest.main() \ No newline at end of file diff --git a/src/devices_controller/.gitignore b/src/devices_controller/.gitignore deleted file mode 100644 index c8a02eb..0000000 --- a/src/devices_controller/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -/client.log -/**/client.log diff --git a/src/devices_controller/README.md b/src/devices_controller/README.md deleted file mode 100644 index 5e4dc62..0000000 --- a/src/devices_controller/README.md +++ /dev/null @@ -1,337 +0,0 @@ - -************************ -## DEVICES & CHANNELS -************************ -VERSION: 0.30.0 - -Date: 19/09/2019 - -By: epallier@irap.omp.eu - - - - - - - -******************************************************************************************** -# 1) TEST - -Pour lancer les TESTS: - - $ cd test/ - $ ./test_client_gemini.py - (test de connexion locale avec un "simulateur" de Gemini) - -Ca doit se terminer par quelque chose comme: -Ran 2 tests in 0.013s -OK - -Là, il faut arrêter avec CTRL-C (car je ne sais pas encore comment arrêter le simulateur de Telescope (serveur) autrement !) - - - -******************************************************************************************** -# 2) RUN - -Pour lancer le client sur le telescope Gemini de Alain Klotz: - - $ cd devices_controller_concrete/device_controller_Gemini/ - $ ./client_telescope_gemini_controller_run.py - (Windows: python3 client_telescope_controller_gemini_run.py) - -(NB: un log "client.log" est créé et alimenté au fur et à mesure) -(Pour avoir moins de détail, mettre la variable DEBUG à False dans le fichier telescope_controller_gemini_run.py) -(press ENTER to quit) - -Examples of requests: - - REQUEST TO SERVER (ENTER to quit): >>> :GR# - NATIVE Command to send is ':GR#' - Sent: b'00090000:GR#\x00' - Sent 13 bytes - RECEIVED (ALL BYTES): b'0009000015:01:48#\x00' - RECEIVED (useful data): 15:01:48 - - REQUEST TO SERVER (ENTER to quit): >>> get ra - GENERIC Command to send is get_ra - NATIVE Command to send is ':GR#' - Sent: b'00100000:GR#\x00' - Sent 13 bytes - RECEIVED (ALL BYTES): b'0010000015:01:48#\x00' - RECEIVED (useful data): 15:01:48 - result is 15:01:48 - - - -******************************************************************************************** -# 3) DEV - -Pour lancer le même client seulement sur le "simulateur" de telescope (localhost, port 11110), ajouter "local" à la fin: - - $ cd devices_controller_concrete/device_controller_Gemini/ - $ ./client_telescope_gemini_controller_run.py local - -Dans un autre terminal, lancer le simulateur: - - $ ./server_telescope_gemini_simulator_run.py - (CTRL-C to stop it) - - -******************************************************************************************** -# 4) DONE - - 5/10/18: - - - greatly improved (plant)UML class diagram(s) - - - 4/10/18: - - - BIG REORGANIZATION OF CODE - => new class DeviceControllerAbstract which has a component ClientChannel (of type "socket", "serial", or "usb") - - tests ok - - cleanup - - - 2/10/18: - - - included "doctests" in tests (try it by running "cd test/ ; ./test_client_gemini.py") - - started plc client - - - improved telescope simulator server answers : do_goto(), do_move(), do_init() - - - improved telescope simulator server answers - - tests pass ok - - - decorator @generic_cmd - - - abstract commands dictionary is now the default one and is ovewritable with native dictionary - - native telescope class is now less than 300 lines (majority of code is in the abstract class so that it is easy to make a new concrete telescope class): - => ne contient quasiment QUE le dictionnaire des commandes natives - - cleanup - - - 1/10/18: - - - interpreteur de cde generique et native : set ra 20:00:00 ou :GR# - - execute_cmd(): - - execute_generic_cmd() - - execute_native_cmd() - - GenericResult() - - - 28/9/18: - - - move do_init() to abstract class - - generic return code for each command - - - clean code - - move do_goto() and move_dir() to abstract class - - - doctests - - - 27/9/18: - - - GOTO - - finalize do_init() - - - set date&time, set lat&long - - README file enriched - - help => liste cdes possibles - - (X) set DATE & TIME (p109) - - Ce que fait AK avec TCL ou C: sprintf(ligne,":SC%02d/%02d/%02d#:SL%02d:%02d:%02.0f#:SG+00#",m,d,y-2000,h,min,s); - 1) :SG+00# - => TU - 2) SC%02d/%02d/%02d#: - mois, jour, YY - 3) SL%02d:%02d:%02.0f#: - hh:mn:ss (heure locale) - m,d,y-2000,h,min,s - Faire les 3 commandes ensemble en 1 seule ligne, chacune séparée par #, le tout terminé par « 00 » - - - generic commands available from client console - - generic commands implemented - - - procedure initialize() - - - ACK (cde 6) - - - 3 types of commands - - \x00 at end - - - classe SocketClientAbstract de base - - - -******************************************************************************************** -5) WORK CURRENTLY IN PROGRESS... - - - main updated - - tests updated - - comment functions args - - - -******************************************************************************************** -6) TODO LIST - - - INTEGRATION INTO PYROS - - - LOG: avoir le nom du module qui logue et non pas only logs.py - - - interpreteur de cde generique (avec celme pour les values) : set ra 20:00:00 - - - Tele MEADE ETX (altaz) : cf doc pdf (from page 1) - - peut être en 3 modes : land, altaz, polar (equatorial) - - :AP# => passer en mode polar - - :AA# => passer en mode AltAz - - cette config doit être fait dans do_init() - - doc page 1 : x=implémenté, p=partial, - = pas implémenté - - - Position class - - - POSITION (p103): - - (100) MOVE() (p104-105) - => en altaz ou radec - => préférer "haut, bas, gauche, droite" plutôt que North, South, west, east (surtout sur altaz ça n'a aucun sens) - => sur equatorial, h,b = DEC et g,d = RA - - set_speed() - - start(direction) avec direction = h,b,g,d => move infini - - stop() - - pulse(direction, nbsec) = start() puis sleep(nbsec) puis stop() - - éventuellement start_diagonal qui ferait start(up) et start(right) en alternance (pas prioritaire) - - MOVE(direction, rate, duration): - - move dans une direction (N,S,E,O) à l'infini - - ':Q#' pour arrêter (p 108) - - duration: infinie si pas donnée - - rate (p108): 4 vitesses différentes (prendre la plus lente par défaut), entre 0 et 1: - - [0-0.25] : RC => Rate Center. Subsequent Move commands will move at Centering Speed. - - [0.25-0.50] : RG => Rate Guide. Subsequent Move commands will move at Guiding Speed. - - [0.50-0.75] : RM => Rate Move. Subsequent Move commands will move at Centering Speed. - - [0.75-1.0] : RS => Rate Slew. Subsequent Move commands will move at Slewing Speed. - - - (100) GOTO: - - bloquant ou non - - 1) goto_setmode(radec | hadec | altaz) - - 2) goto() - - Attention, gemini ne comprend que radec, les tarot ne comprennent que hadec !!! : - => goto_setmode(radec) ne fait rien sur un gemini, mais convertit sur un tarot - - - (100) vitesse de pointage (slew speed) : n'existe pas sur tous les tele - - - drift = tracking speed : une fois que tele a pointé, comment il part... - - toujours en hadec - - tracking_speed = 0 => tele est fixe - - t.tracking_speed(ha ou ra, dec) => ha est en nb degrés/sec - => t.tracking_speed(0,0) - => t.tracking_speed("diurnal" ou "sideral",0) // sideral c'est mieux - - jour solaire : 86400s/jour - - jour sideral (diurnal) = durée d'une journée pour une étoile = 86164s/jour (moins que jour solaire, à cause du fait que en 24h, la Terre a avancé sur son orbite circulaire) - - nb degrés/sec = 360/86400 pour jour solaire, et 360/86164 pour jour sideral - - - - (200) timeout : 1 pour send, 1 pour receive - - - (200) celme.angles => infos() à généraliser - - - (200) type erreurs - - 1000 : communication (network, cable débranché) = channel - - 2000 : syntax - - 3000 : out of limit (pointage hors limite, ex: sur le sol) - - 4000 : controller - - 5000 : file (disk...) - - - (200) generic functions return tuple with error code : - res = get_ra() - => res.res is the result - => res.error is the error object : err.error_code, err.generic_msg, err.native_msg - => ou plutot: - res,error = get_ra() - res,_ = get_ra() # pour ignorer le code erreur - res is the result - error is the error object - - - (200) PLC abstract client - - - (100) input commands format : - - get ra - - set ra "2h3m27s" => converted by celme.Angle(ra).deg() - - - (100) _cmds = list of functions (???) - - - remplacer utf-8 par unicode ou iso... - - - cdes 05 (p100): - - cde 05 => return long list of parameters (= GROS GET) - - ENQ = 05 - - - Table d'attributs pour chaque telescope (config) - - - _connect() ou connect() ? - - - Implémenter les commandes NATIVES (non LX-200) : - - < ou >, termine par ':' + checksum + # - - - - - - - - - - - - -******************************************************************************************** -7) INFORMATIONS - - - GMT = TU décalé de 12h, mais maintenant c'est pareil - TU = UTC civil, voir aussi UT1, UT2 - - COMMANDES LX-200: - - SA et SZ pour envoyer coord en alt-az - - RA-DEC is converted into "pas codeurs" - 1 pas codeur (mvmt interne du tele) = environ 1 sec arc sur le ciel - (en gros, RA = petit axe du tele, DEC = grand axe du tele) - - Parking = vers le nord (cf photo) = en position CWD (Counter Weight Down) (contre-poids en bas ?) - - Par défaut, le tele fonctionne en RADEC J2000 - - http://82.64.28.71:8083/L5V1serial.html - - - Ce qui marche: - - - REQUEST TO SERVER [ex: ':GD#' (Get Dec), ':GR#' (Get RA)']: 01000000:GD# - Received (all data): 01000000+90:00:00# - - - REQUEST TO SERVER [ex: ':GD#' (Get Dec), ':GR#' (Get RA)']: 01000000:GR# - Received (all data): 0100000015:01:48# - - REQUEST TO SERVER [ex: ':GD#' (Get Dec), ':GR#' (Get RA)']: 01000000:GR# - Received (all data): 0100000016:04:17# - - - Get Software Level l(one digit) and Version vv(two digits) - REQUEST TO SERVER [ex: ':GD#' (Get Dec), ':GR#' (Get RA)']: 01000000:GV# - Received (all data): 01000000510# - - - REQUEST TO SERVER [ex: ':GD#' (Get Dec), ':GR#' (Get RA)']: 01000000:GG# - Received (all data): 01000000+00# - - - Get Maximum Velocity of both axes (N = No mvmt) - REQUEST TO SERVER [ex: ':GD#' (Get Dec), ':GR#' (Get RA)']: 01000000:Gv# - Received (all data): 01000000N - - - - - - - - - diff --git a/src/devices_controller/__init__.py b/src/devices_controller/__init__.py deleted file mode 100644 index efcff89..0000000 --- a/src/devices_controller/__init__.py +++ /dev/null @@ -1 +0,0 @@ -#default_app_config = "agent.apps.AgentConfig" diff --git a/src/devices_controller/channels/client_channel.py b/src/devices_controller/channels/client_channel.py deleted file mode 100755 index cedf304..0000000 --- a/src/devices_controller/channels/client_channel.py +++ /dev/null @@ -1,118 +0,0 @@ -#!/usr/bin/env python3 - -"""Socket Client (abstract) - -To be used as a base class (interface) for any concrete socket client class -""" - - -# Standard library imports - -# Third party imports -# None - -# Local application imports -from devices_controller.logs import * - - - -##class SocketClientAbstract(): -class ClientChannel(): - - my_channel = None - buf = 1024 - - def __init__(self, server_host:str="localhost", server_port:int=11110, PROTOCOL:str="SOCKET-TCP", buffer_size=1024, DEBUG=False): - ''' - :param server_host: server IP or hostname - :param server_port: server port - :param PROTOCOL: "SOCKET-TCP" or "SOCKET-UDP" or "SERIAL" or "USB" (to be continued...) - ''' - self.DEBUG = DEBUG - - self.HOST = server_host - self.PORT = server_port - self.PROTOCOL = PROTOCOL - self.buf = buffer_size - - # Logger configuration - #self.set_logger() - ##set_logger(DEBUG) - log_d("\n**************************") - log_d("Client CHANNEL instanciated") - - - - # So that we can use this with the "with" statement (context manager) - def __enter__(self): - return self - def __exit__(self, type, value, traceback): - self.close() - #log_d("Client channel killed") - print("Client channel killed") - - - def send_data(self, data:str): - ##data_encapsulated = self.encapsulate_data_to_send(data) - self._send_data(data) - ''' - #data_to_send = bytes(data_to_send + "\n", "utf-8") - #data_to_send = bytes(data_to_send + "\x00", "utf-8") - ##data_to_send = bytes(data_to_send, "utf-8") - data_to_send_bytes = data_to_send.encode("utf-8") - #data_to_send_bytes = data_to_send.encode() - if self.PROTOCOL=="SOCKET-TCP": - # Unlike send(), this method sendall() continues to send data from bytes until either all data has been sent or an error occurs. - # None is returned on success. - # On error, an exception is raised, and there is no way to determine how much data, if any, was successfully sent - nb_bytes_sent = self.mysock.sendall(data_to_send_bytes) - else: - # SOCKET-UDP - nb_bytes_sent = self.mysock.sendto(data_to_send_bytes, (self.HOST, self.PORT)) - log_i(f'Sent: {data_to_send_bytes}') - log_i(f"Sent {nb_bytes_sent} bytes") - ''' - #@abstract - def _send_data(self, data_to_send:str): - pass - - def receive_data(self)->str: - data_received = self._receive_data() - ##data_received_bytes = self.mysock.recv(self.buf) - #log_d("Received (all data): {}".format(data_received)) - #log_d("data in bytes: "+str(bytes(data_received, "utf-8"))) - ##data_received_uncaped = self.uncap_received_data(data_received) - #log_i("RECEIVED (useful data): {}".format(data_received)) - return data_received - #@abstract - def _receive_data(self)->bytes: - pass - ''' - data_received = self.mysock.recv(self.buf) - #log_d("data type is "+str(type(data_received))) - log_i(f"RECEIVED (ALL BYTES): {data_received}") - #return str(data_received, "utf-8") - return data_received - ''' - - #@abstract - def _connect_to_server(self): - pass - - # Close socket - def close(self): - self.mysock.close() - - - ''' - put(), read(), put_read() commands - ''' - def put(self, data:str): - self.send_data(data) - def read(self)->str: - return self.receive_data() - def put_read(self, data:str)->str: - # send command - self.put(data) - # receive answer (or ack) - return self.read() diff --git a/src/devices_controller/channels/client_channel_serial.py b/src/devices_controller/channels/client_channel_serial.py deleted file mode 100755 index 73d493f..0000000 --- a/src/devices_controller/channels/client_channel_serial.py +++ /dev/null @@ -1,51 +0,0 @@ -#!/usr/bin/env python3 - -"""SERIAL Client implementation - -""" - - -# Standard library imports - -# Third party imports -# None - -# Local application imports -from devices_controller.channels.client_channel import * - - - - -class ClientChannelSerial(ClientChannel): - - buf = 1024 - - - def __init__(self, server_host:str="localhost", server_port:int=11110, buffer_size=1024, DEBUG:bool=False): - ''' - :param server_host: server IP or hostname - :param server_port: server port - ''' - #TODO: set a protocol - self.protocol = None - super().__init__(server_host, server_port, "SERIAL", buffer_size, DEBUG) - - - - #@override - def _connect_to_server(self): - pass - - # Close socket - def close(self): - self.mysock.close() - - #@override - def _send_data(self, data_to_send:str): - pass - - #@override - def _receive_data(self)->str: - data_received = self.mysock.recv(self.buf) - return data_received - diff --git a/src/devices_controller/channels/client_channel_socket.py b/src/devices_controller/channels/client_channel_socket.py deleted file mode 100755 index 8b0d26c..0000000 --- a/src/devices_controller/channels/client_channel_socket.py +++ /dev/null @@ -1,181 +0,0 @@ -#!/usr/bin/env python3 - -"""Socket Client implementation (TCP or UDP) - -""" - - -# Standard library imports -import socket - -# Third party imports -# None - -# Local application imports -from devices_controller.channels.client_channel import * - - - - -##class SocketClientAbstract(): -class ClientChannelSocket(ClientChannel): - - # Select protocol here (udp or tcp) - buf = 1024 - mysock = None - - # STAMP : - # 00 00 00 00 00 00 00' (8 bytes from '00' to 'FF', 1st one is LOW BYTE) - # ex: 01 + STAMP = 01 00 00 00 00 00 00 00' (8 bytes from '00' to 'FF', 1st one is LOW BYTE) - #STAMP_FILLER = '00000000000000' - ''' - STAMP_FILLER = '000000' - MYSTAMP = '01' - MY_FULL_STAMP = MYSTAMP + STAMP_FILLER - ''' - - - - def __init__(self, server_host:str="localhost", server_port:int=11110, PROTOCOL:str="SOCKET-TCP", buffer_size=1024, DEBUG=False): - ''' - :param server_host: server IP or hostname - :param server_port: server port - :param PROTOCOL: "SOCKET-UDP" or "SOCKET-TCP" - ''' - myprotocol = socket.SOCK_DGRAM if PROTOCOL=="SOCKET-UDP" else socket.SOCK_STREAM - self.mysock = socket.socket(socket.AF_INET, myprotocol) - super().__init__(server_host, server_port, PROTOCOL, buffer_size, DEBUG) - # Logger configuration - #self.set_logger() - ''' - log_d("\n**************************") - log_d("Logger configured") - log_d("Client instanciated") - ''' - - - ''' - def _send_data(self, data_to_send:str): - #data_to_send = bytes(data_to_send + "\n", "utf-8") - #data_to_send = bytes(data_to_send + "\x00", "utf-8") - ##data_to_send = bytes(data_to_send, "utf-8") - #print("before sending", data_to_send) - data_to_send_bytes = data_to_send.encode("utf-8") - #data_to_send_bytes = data_to_send.encode() - #print("before sending", data_to_send_bytes) - if self.PROTOCOL=="SOCKET-TCP": - # Unlike send(), this method sendall() continues to send data from bytes until either all data has been sent or an error occurs. - # None is returned on success. - # On error, an exception is raised, and there is no way to determine how much data, if any, was successfully sent - nb_bytes_sent = self.mysock.sendall(data_to_send_bytes) - else: - nb_bytes_sent = self.mysock.sendto(data_to_send_bytes, (self.HOST, self.PORT)) - log_i(f'Sent: {data_to_send_bytes}') - log_i(f"Sent {nb_bytes_sent} bytes") - ''' - ''' - def _receive_data(self)->bytes: - data_received = self.mysock.recv(self.buf) - #log_d("data type is "+str(type(data_received))) - log_i(f"RECEIVED (ALL BYTES): {data_received}") - #return str(data_received, "utf-8") - return data_received - ''' - - # Only for TCP ; does nothing for UDP - #@override - def _connect_to_server(self): - if self.PROTOCOL=="SOCKET-TCP": self.mysock.connect((self.HOST, self.PORT)) - print(f"Ready to send commands to HOST {self.HOST} on PORT {self.PORT} \n") - - - # Close socket - def close(self): - self.mysock.close() - - #@override - def _send_data(self, data_to_send:str): - #data_to_send = bytes(data_to_send + "\n", "utf-8") - #data_to_send = bytes(data_to_send + "\x00", "utf-8") - ##data_to_send = bytes(data_to_send, "utf-8") - data_to_send_bytes = data_to_send.encode("utf-8") - #data_to_send_bytes = data_to_send.encode() - if self.PROTOCOL=="SOCKET-TCP": - # Unlike send(), this method sendall() continues to send data from bytes until either all data has been sent or an error occurs. - # None is returned on success. - # On error, an exception is raised, and there is no way to determine how much data, if any, was successfully sent - nb_bytes_sent = self.mysock.sendall(data_to_send_bytes) - else: - # SOCKET-UDP - nb_bytes_sent = self.mysock.sendto(data_to_send_bytes, (self.HOST, self.PORT)) - log_i(f'Sent: {data_to_send_bytes}') - log_i(f"Sent {nb_bytes_sent} bytes") - - #@override - def _receive_data(self)->str: - data_received_bytes = self.mysock.recv(self.buf) - #TODO: resoudre ce pb plus proprement (utiliser unicode au lieu de utf-8 codec ??? - # BUGFIX: b'\xdf' (should correspond to "°" symbol) generates a UnicodeDecodeError, - # so, replace it by ':' (b'\x3A') - if b'\xdf' in data_received_bytes: - data_received_bytes = data_received_bytes.replace(b'\xdf', b'\x3A') - log_i(f"RECEIVED (ALL BYTES): {data_received_bytes}") - - data_received = data_received_bytes.decode() - #log_d("data type is "+str(type(data_received))) - #return str(data_received, "utf-8") - return data_received - - ''' - # Encapsulate useful data to be ready for sending - #@override - def encapsulate_data_to_send(self, data:str): - #return bytes(data, "utf-8") - return data.encode("utf-8") - # Extract data from received raw data - #@override - def uncap_received_data(self, data_received_bytes:bytes): - #return data_received.decode("utf-8) - return data_received_bytes.decode() - ''' - - -# TODO: empecher de creer une instance de cette classe abstraite -# Avec ABC ? - -''' -if __name__ == "__main__": - - #HOST, PORT = "localhost", 9999 - #HOST, PORT = "localhost", 20001 - HOST, PORT = "localhost", 11110 - - # Classic usage: - #tsock = SocketClient_UDP_TCP(HOST, PORT, "UDP") - # More elegant usage, using "with": - with SocketClient_ABSTRACT(HOST, PORT, "UDP") as tsock: - - # 0) CONNECT to server (only for TCP, does nothing for UDP) - tsock._connect_to_server() - - while True: - - # 1) SEND REQUEST data to server - # saisie de la requête au clavier et suppression des espaces des 2 côtés - data = input("REQUEST TO SERVER [ex: ':GD#' (Get Dec), ':GR#' (Get RA)']: ").strip() - # test d'arrêt - if data=="": break - #data_to_send = bytes(data + "\n", "utf-8") - tsock.send_data(data) - #mysock.sendto("%s" % data, (HOST, PORT)) - #print("Sent: {}".format(data)) - - # 2) RECEIVE REPLY data from server - data_received = tsock.receive_data() - #reponse, adr = mysock.recvfrom(buf) - #print("Received: {}".format(data_received)) - #print("Useful data received: {}".format(data_useful)) - print('\n') - - #tsock.close() -''' \ No newline at end of file diff --git a/src/devices_controller/channels/client_channel_usb.py b/src/devices_controller/channels/client_channel_usb.py deleted file mode 100755 index 623cca1..0000000 --- a/src/devices_controller/channels/client_channel_usb.py +++ /dev/null @@ -1,51 +0,0 @@ -#!/usr/bin/env python3 - -"""SERIAL Client implementation - -""" - - -# Standard library imports - -# Third party imports -# None - -# Local application imports -from devices_controller.channels.client_channel import * - - - - -class ClientChannelUSB(ClientChannel): - - buf = 1024 - - - def __init__(self, server_host:str="localhost", server_port:int=11110, buffer_size=1024, DEBUG=False): - ''' - :param server_host: server IP or hostname - :param server_port: server port - ''' - #TODO: set a protocol - self.protocol = None - super().__init__(server_host, server_port, "USB", buffer_size, DEBUG) - - - - #@override - def _connect_to_server(self): - pass - - # Close socket - def close(self): - self.mysock.close() - - #@override - def _send_data(self, data_to_send:str): - pass - - #@override - def _receive_data(self)->str: - data_received = self.mysock.recv(self.buf) - return data_received - diff --git a/src/devices_controller/devices_controller_abstract_component/device_controller_abstract.py b/src/devices_controller/devices_controller_abstract_component/device_controller_abstract.py deleted file mode 100755 index d63ecb6..0000000 --- a/src/devices_controller/devices_controller_abstract_component/device_controller_abstract.py +++ /dev/null @@ -1,563 +0,0 @@ -#!/usr/bin/env python3 - -"""Socket Client Telescope (abstract) implementation - -To be used as a base class (interface) for any concrete socket client telescope class -""" - - - - -# Standard library imports -#from enum import Enum -import functools -import logging -import socket -import sys -import time - -# Third party imports - -# from sockets_tele/ -sys.path.append("..") -# from src_socket/client/ -sys.path.append("../../..") -#import src.core.pyros_django.utils.celme as celme -import src.core.celme as celme -from devices_controller.logs import * - - -# Local application imports -#sys.path.append('../..') -#from src.client.socket_client_abstract import UnknownCommandException, SocketClientAbstract -##from src_socket.client.socket_client_abstract import * -##from src_device.client.client_channel import * -from devices_controller.channels.client_channel_socket import ClientChannelSocket -from devices_controller.channels.client_channel_serial import ClientChannelSerial -from devices_controller.channels.client_channel_usb import ClientChannelUSB - - - - -# Execute also "set" and "do" commands -GET_ONLY=False -# Execute only "get" commands -#GET_ONLY=True - -# Default timeouts -TIMEOUT_SEND = 10 -TIMEOUT_RECEIVE = 10 - - -''' -class c(Enum): - - # GET, SET - DEC = 'DEC' - RA = 'RA' - RA_DEC = 'RA_DEC' - - # DO - PARK = 'PARK' - WARM_START = 'WARM_START' -''' - -# DECORATOR -def generic_cmd(func): - #def wrapper_generic_cmd(*args, **kwargs): - @functools.wraps(func) - def wrapper_generic_cmd(self, values_to_set=None): - #print("func name is", func.__name__) - return self.execute_generic_cmd(func.__name__, values_to_set) - return wrapper_generic_cmd - - - -class GenericResult: - ''' Usage: - res = execute(command) - print("result is", res) - if res.ko: raise UnexpectedReturnCode() - if res.ok: - ... - ''' - # By default, bad result - ok = True - ko = False - - def __init__(self, native_result:str, ok=True): - self.txt = native_result - self.ok = ok - self.ko = not ok - def __str__(self): - return self.txt - ''' - def __repr__(self): - return self.txt - def __get__(self, instance, owner): - return self.b - def __set__(self, instance, value): - self.b = value - ''' - - - -class UnexpectedCommandReturnCode(Exception): - pass -class TimeoutException(Exception): - pass -class UnknownCommandException(Exception): - pass - ''' - def __init__(self,*args,**kwargs): - super().__init__(self,*args,**kwargs) - ''' - - - -#TODO: remove ClientChannelAbstract, and set instead a ClientChannel -#class DeviceControllerAbstract(SocketClientAbstract): -##class DeviceControllerAbstract(ClientChannel): -class DeviceControllerAbstract(): - - # ClientChannel used by the device controller (to be set during __init__ via set_client_channel()) - my_channel = None - - # @abstract (to be overriden) - _cmd_device_concrete = {} - _cmd_device_abstract = {} - _cmd = { - # GET-SET commands: - - 'get_timezone': [], - 'set_timezone': [], - - 'get_date': [], - 'set_date': [], - - 'get_time': [], - 'set_time': [], - - # DO commands: - 'do_init': ['do_init'], - 'do_park': [], - } - - - ##def __init__(self, device_host:str="localhost", device_port:int=11110, PROTOCOL:str="TCP", buffer_size=1024, DEBUG=False): - def __init__(self, device_host:str="localhost", device_port:int=11110, PROTOCOL:str="SOCKET-TCP", buffer_size=1024, DEBUG=False): - ''' - :param device_host: server IP or hostname - :param device_port: server port - :param PROTOCOL: "SOCKET-TCP", "SOCKET-UDP", "SERIAL", or "USB" - ''' - ##super().__init__(device_host, device_port, PROTOCOL, buffer_size, DEBUG) - set_logger(DEBUG) - log_d("Logger configured") - - if PROTOCOL.startswith("SOCKET"): self.my_channel:ClientChannel = ClientChannelSocket(device_host, device_port, PROTOCOL, buffer_size, DEBUG) - elif PROTOCOL == "SERIAL": self.my_channel:ClientChannel = ClientChannelSerial(device_host, device_port, buffer_size, DEBUG) - elif PROTOCOL == "USB": self.my_channel:ClientChannel = ClientChannelUSB(device_host, device_port, buffer_size, DEBUG) - else: raise Exception("Unknown Channel", PROTOCOL) - - # overwrite abstract _cmd dictionary with subclass native _cmd_native dictionary: - #self._cmd = {**self._cmd, **self._cmd_native} - self._cmd = {**self._cmd, **self._cmd_device_abstract, **self._cmd_device_concrete} - - - # So that we can use this with the "with" statement (context manager) - def __enter__(self): - return self - def __exit__(self, type, value, traceback): - self.my_channel.__exit__(type, value, traceback) - - ''' - def set_logger(self, DEBUG): - self.my_channel.set_logger(DEBUG) - ''' - - def _connect_to_device(self): - self.my_channel._connect_to_server() - - def get_celme_longitude(self, longitude): - return celme.Angle(longitude).sexagesimal("d:+0180.0") - def get_celme_latitude(self, latitude): - return celme.Angle(latitude).sexagesimal("d:+090.0") - - #@override ClientChannel send_data - def send_data(self, data:str): - data_encapsulated = self.format_data_to_send(data) - self.my_channel.send_data(data_encapsulated) - ''' - The chosen way to send data is this: - # - :GD# - b'00030000:GD#\x00' - - Another way to send data (which also works), is it better ? - # - :GD# - ###tsock.mysock.sendto(b'\x00\x00\x00\x01\x00\x00\x00\x00\x3A\x47\x44\x23\x00', (HOST, PORT)) - # - :GR# - ###tsock.mysock.sendto(b'\x00\x00\x00\x01\x00\x00\x00\x00\x3A\x47\x52\x23\x00', (HOST, PORT)) - # - ACK 06 OK !!! : - tsock.mysock.sendto(b'\x00\x00\x00\x01\x00\x00\x00\x00\x00\x06\x00\x00', (HOST, PORT)) - - Which one is the best method ? - ''' - #log_d("NATIVE Command to send is "+repr(data)) - ##encapsulated_data = self.encapsulate_data_to_send(data) - #print("before _send", encapsulated_data) - #print("before _send", repr(encapsulated_data)) - ##self._send_data(encapsulated_data) - ##self.my_channel.send_data(encapsulated_data) - #log_i(f'Sent: {encapsulated_data}') - ''' - def _send_data(self, data): - self.my_channel._send_data(data) - ''' - - #@override ClientChannel receive_data - def receive_data(self)->str: - ##data_received_bytes = self._receive_data() - data_received = self.my_channel.receive_data() - #log_d("Received (all data): {}".format(data_received)) - #log_d("data in bytes: "+str(bytes(data_received, "utf-8"))) - data = self.unformat_received_data(data_received) - log_i("RECEIVED (useful data): {}".format(data)) - return data - ''' - def _receive_data(self): - return self.my_channel._receive_data() - ''' - - # Encapsulate useful data to be ready for sending - # By default, do nothing - #@abstract - def format_data_to_send(self, data:str): - return self.encapsulate_data_to_send(data) - #@deprecated - def encapsulate_data_to_send(self, data:str): - return data - - # Extract useful data from received raw data - # By default, do nothing - #@abstract - def unformat_received_data(self, data:str): - return self.uncap_received_data(data) - #@deprecated - def uncap_received_data(self, data:str): - #return data_received.decode() - return data - - ''' - def encapsulate_data_to_send(self, command:str): - return self.my_channel.encapsulate_data_to_send(command) - def uncap_received_data(self, data_received:str): - return self.my_channel.uncap_received_data(data_received) - ''' - - - def get_utc_date(self): - return celme.Date("now").iso(0) - #return celme.Date("now").ymdhms() - - - def close(self): - self.my_channel.close() - - - def is_generic_cmd(self, raw_input_cmd:str) -> bool: - # Using Google documentation format (https://www.sphinx-doc.org/en/master/usage/extensions/example_google.html#example-google) - """ Is this a generic command ? - - Args: - raw_input_cmd: a command in raw string format (like 'get ra' or 'set ra 20:00:00' or 'set radec 20:00:00 90:00:00" or 'do park' or 'do_park'...) - - Returns: - either False or (cmd, [values]) with cmd like "get_ra" (using underscore '_' instead of space ' '). - - """ - #return cmd.startswith('get_') or cmd.startswith('set_') or cmd.startswith('do_') - #cmds = ['get ', 'get_', 'set ', 'set_', 'do ', 'do_'] - ''' - seps = (" ", "_") - #cmds = list(x+y for x in cmd for y in sep) - for cmd in cmds: - for sep in seps: - generic_cmd = cmd+sep - if raw_input_cmd.startswith(generic_cmd): - # Is there value(s) passed ? - if len(raw_input_cmd) > len(generic_cmd): - values = raw_input_cmd[len(generic_cmd):] - values = values.split(' ') - # return cmd like "get_ra", [and values] - return generic_cmd.replace(' ','_'), values - return False, False - ''' - values_to_set = None - cmds = ("get","set","do") - raw_input_cmd = raw_input_cmd.strip() - cmd_splitted = raw_input_cmd.split(' ') - if len(cmd_splitted) == 1: return False,False - # ex: "set_radec" - generic_cmd = cmd_splitted[0] + '_' + cmd_splitted[1] - # Check this generic command exists - #if (generic_cmd not in self._cmd.keys()): return False,False - if generic_cmd not in self._cmd: return False,False - # Is there value(s) passed ? - if len(cmd_splitted) > 2: values_to_set = cmd_splitted[2:] - # ex: return "set_radec", ["20:00:00", "90:00:00"] - return generic_cmd, values_to_set - - - def execute_cmd(self, raw_input_cmd:str)->GenericResult: - # GENERIC command - generic_cmd, values = self.is_generic_cmd(raw_input_cmd) - if generic_cmd is not False: - #print("GENERIC COMMAND") - return self.execute_generic_cmd(generic_cmd, values) - else: - ''' - if cmd.startswith('get_'): - #generic_cmd,_ = request[4:].split('(') - generic_cmd = cmd[4:] - if (generic_cmd not in self._cmd_getset.keys()) and (generic_cmd not in self._cmd_do.keys()): - #eval(request) - return self.get_radec() - print("cmd is", generic_cmd) - return self._get(generic_cmd) - return - ''' - # NATIVE command - #print("NATIVE COMMAND") - res_native = self.execute_native_cmd(raw_input_cmd) - return GenericResult(res_native) - - - #def execute_native_cmd(self, request:str, awaited_res_if_ok=None)->GenericResult: - def execute_native_cmd(self, native_cmd:str) -> str: - """ Execute a native command - - Args: - native_cmd: a native command - - Returns: - the command result - - """ - print("NATIVE Command to send is "+ repr(native_cmd)) - #self.send_request(native_cmd) - self.send_native_cmd(native_cmd) - native_res = self.receive_data() - return native_res - ''' - ok = True if not awaited_res_if_ok else (native_res == awaited_res_if_ok) - return GenericResult(native_res, ok) - ''' - - - ''' - def execute_native_cmd_OLD(self, request:str)->str: - self.send_request(request) - native_res = self.receive_data() - return native_res - ''' - - - def execute_unformated_native_cmd(self, request:str) -> str: - request = self.formated_cmd(request) - #return self.execute_native_cmd_OLD(request) - return self.execute_native_cmd(request) - - - def send_native_cmd(self, native_cmd:str)->str: - return self.send_data(native_cmd) - - - #@deprecated - def send_request(self, request:str)->str: - return self.send_native_cmd(request) - - - def print_available_commands(self): - print("\nAvailable commands are:") - print("- GET commands:") - print (list(cmd.replace('_',' ') for cmd in self._cmd.keys() if cmd.startswith('get_'))) - print("- SET commands:") - print (list(cmd.replace('_',' ') for cmd in self._cmd.keys() if cmd.startswith('set_'))) - print("- DO commands:") - print (list(cmd.replace('_',' ') for cmd in self._cmd.keys() if cmd.startswith('do_'))) - - - def available_commands(self): - return list(self._cmd.keys()) - - - # @abstract - def formated_cmd(self, cmd:str, value:str=None)->str: - return cmd - - - #def run_func(self, func, arg=None): - def run_func(self, func, *args): - #print("args", args) - if args: - return getattr(self, func)(*args) - else: - return getattr(self, func)() - - ''' - TELESCOPE COMMANDS (abstract methods) - ''' - - def execute_generic_cmd(self, generic_cmd:str, values_to_set:str=None)->str: - ''' Execute a generic command - - :param generic_cmd: str like "get_ra" or "set_ra" or "do_park"... - :param value: only for a "set_" cmd - ''' - - #log_d("\n\nGENERIC Command to send is "+generic_cmd) - print("\n\nGENERIC Command to send is "+generic_cmd) - # Check if generic_param exists - #if generic_cmd not in self._cmd.keys(): raise UnknownCommandException() - # if this generic command has no corresponding native command, raise NotImplementedError - native_cmd_infos = self._cmd[generic_cmd] - if not native_cmd_infos: raise NotImplementedError - # Get corresponding native command: - native_cmd = native_cmd_infos[0] - if not native_cmd: raise NotImplementedError - # ex: native_cmd == "do_init", "get_radec" - if native_cmd == generic_cmd: - #print("cmd,val", native_cmd, values_to_set) - #res:GenericResult = self.run_func(native_cmd, *values_to_set) - if values_to_set: - res = self.run_func(native_cmd, *values_to_set) - #res = getattr(self, native_cmd)(values_to_set) - else: - res = self.run_func(native_cmd) - #res = getattr(self, native_cmd)() - #if res is None: res = 'ok' - # res should be a GenericResult - if not isinstance(res,GenericResult): raise Exception("Should be a GenericResult", res) - return res - # ex: native_cmd == "GR" - else: - native_cmd = self.formated_cmd(native_cmd,values_to_set) - - awaited_res_if_ok = None - if len(native_cmd_infos) > 1: awaited_res_if_ok = native_cmd_infos[1] - #native_res = self.execute_native_cmd(self.formated_cmd(native_cmd,value), awaited_res_ok) - native_res = self.execute_native_cmd(native_cmd) - ok = True if not awaited_res_if_ok else (native_res == awaited_res_if_ok) - return GenericResult(native_res, ok) - - - - ''' - **************************** - **************************** - GENERIC TELESCOPE COMMANDS (abstract methods) - **************************** - **************************** - ''' - - - - ''' - **************************** - GENERIC GET & SET commands - **************************** - ''' - - - @generic_cmd - def get_timezone(self): pass - #def get_timezone(self): return self.execute_generic_cmd('get_timezone') - @generic_cmd - def set_timezone(self, hh): pass - #def set_timezone(self, hh): return self.execute_generic_cmd('set_timezone', hh) - - @generic_cmd - def get_date(self): pass - @generic_cmd - def set_date(self, mmddyy): pass - - @generic_cmd - def get_time(self): pass - @generic_cmd - def set_time(self, hhmmss): pass - - - - ''' - **************************** - GENERIC DO commands - **************************** - ''' - - # @abstract - #def do_INIT(self): return self._do("INIT") - - ''' do_PARK() (p103) - - STARTUP position = CWD - - :hC# - - position required for a Cold or Warm Start, pointing to the celestial pole of the given hemisphere (north or south), - with the counterweight pointing downwards (CWD position). From L4, V1.0 up - - HOME position parking => par defaut, c'est CWD, mais ca peut etre different - - :hP# - - defaults to the celestial pole visible at the given hemisphere (north or south) and can be set by the user - ''' - # @abstract - def do_PARK(self): pass - #def do_PARK(self): return self._do("PARK") - - # @abstract - def do_start(self): pass - def do_stop(self): pass - - # @abstract MACRO - def do_init(self): return NotImplementedError - - - - - -# TODO: empecher de creer une instance de cette classe abstraite -# Avec ABC ? - -''' -if __name__ == "__main__": - - #HOST, PORT = "localhost", 9999 - #HOST, PORT = "localhost", 20001 - HOST, PORT = "localhost", 11110 - - # Classic usage: - #tsock = SocketClient_UDP_TCP(HOST, PORT, "UDP") - # More elegant usage, using "with": - with SocketClient_ABSTRACT(HOST, PORT, "UDP") as tsock: - - # 0) CONNECT to server (only for TCP, does nothing for UDP) - tsock._connect_to_server() - - while True: - - # 1) SEND REQUEST data to server - # saisie de la requête au clavier et suppression des espaces des 2 côtés - data = input("REQUEST TO SERVER [ex: ':GD#' (Get Dec), ':GR#' (Get RA)']: ").strip() - # test d'arrêt - if data=="": break - #data_to_send = bytes(data + "\n", "utf-8") - tsock.send_data(data) - #mysock.sendto("%s" % data, (HOST, PORT)) - #print("Sent: {}".format(data)) - - # 2) RECEIVE REPLY data from server - data_received = tsock.receive_data() - #reponse, adr = mysock.recvfrom(buf) - #print("Received: {}".format(data_received)) - #print("Useful data received: {}".format(data_useful)) - print('\n') - - #tsock.close() -''' \ No newline at end of file diff --git a/src/devices_controller/devices_controller_abstract_component/device_controller_detector_sensor.py b/src/devices_controller/devices_controller_abstract_component/device_controller_detector_sensor.py deleted file mode 100755 index 7fa6a47..0000000 --- a/src/devices_controller/devices_controller_abstract_component/device_controller_detector_sensor.py +++ /dev/null @@ -1,78 +0,0 @@ -#!/usr/bin/env python3 - -""" -Filter selector (abstract) implementation - -To be used as a base class (interface) for any concrete Filter selector class -""" - - -import sys - -# from sockets_tele/ -sys.path.append("..") -# from src_socket/client/ -#import src.core.pyros_django.utils.celme as celme - -sys.path.append("../../..") -import src.core.celme as celme - - -# Local application imports -#from devices_controller.devices_controller_abstract_component.device_controller_abstract import * -sys.path.append("../..") -from devices_controller.devices_controller_abstract_component.device_controller_abstract import * - - -# Default timeouts -TIMEOUT_SEND = 10 -TIMEOUT_RECEIVE = 10 - - - -#class SocketClientTelescopeAbstract(SocketClientAbstract): -class DeviceControllerDetectorSensor(DeviceControllerAbstract): - - # @abstract (to be overriden) - _cmd_device_concrete = {} - _cmd_device_abstract = {} - - - #TODO: remplacer PROTOCOL par "SOCKET-TCP", "SOCKET-UDP", "SERIAL", ou "USB" - def __init__(self, device_host:str="localhost", device_port:int=11110, PROTOCOL:str="TCP", buffer_size=1024, DEBUG=False): - ''' - :param device_host: server IP or hostname - :param device_port: server port - :param PROTOCOL: "UDP" or "TCP" - ''' - super().__init__(device_host, device_port, PROTOCOL, buffer_size, DEBUG) - # overwrite abstract _cmd dictionary with subclass native _cmd_native dictionary: - #self._cmd = {**self._cmd, **self._cmd_native} - - - - - ''' - **************************** - **************************** - GENERIC TELESCOPE COMMANDS (abstract methods) - **************************** - **************************** - ''' - - - - ''' - **************************** - GENERIC GET & SET commands - **************************** - ''' - - - ''' - **************************** - GENERIC DO commands - **************************** - ''' - - diff --git a/src/devices_controller/devices_controller_abstract_component/device_controller_detector_shutter.py b/src/devices_controller/devices_controller_abstract_component/device_controller_detector_shutter.py deleted file mode 100755 index f207b0b..0000000 --- a/src/devices_controller/devices_controller_abstract_component/device_controller_detector_shutter.py +++ /dev/null @@ -1,78 +0,0 @@ -#!/usr/bin/env python3 - -""" -Filter selector (abstract) implementation - -To be used as a base class (interface) for any concrete Filter selector class -""" - - -import sys - -# from sockets_tele/ -sys.path.append("..") -# from src_socket/client/ -#import src.core.pyros_django.utils.celme as celme - -sys.path.append("../../..") -import src.core.celme as celme - - -# Local application imports -#from devices_controller.devices_controller_abstract_component.device_controller_abstract import * -sys.path.append("../..") -from devices_controller.devices_controller_abstract_component.device_controller_abstract import * - - -# Default timeouts -TIMEOUT_SEND = 10 -TIMEOUT_RECEIVE = 10 - - - -#class SocketClientTelescopeAbstract(SocketClientAbstract): -class DeviceControllerDetectorShutter(DeviceControllerAbstract): - - # @abstract (to be overriden) - _cmd_device_concrete = {} - _cmd_device_abstract = {} - - - #TODO: remplacer PROTOCOL par "SOCKET-TCP", "SOCKET-UDP", "SERIAL", ou "USB" - def __init__(self, device_host:str="localhost", device_port:int=11110, PROTOCOL:str="TCP", buffer_size=1024, DEBUG=False): - ''' - :param device_host: server IP or hostname - :param device_port: server port - :param PROTOCOL: "UDP" or "TCP" - ''' - super().__init__(device_host, device_port, PROTOCOL, buffer_size, DEBUG) - # overwrite abstract _cmd dictionary with subclass native _cmd_native dictionary: - #self._cmd = {**self._cmd, **self._cmd_native} - - - - - ''' - **************************** - **************************** - GENERIC TELESCOPE COMMANDS (abstract methods) - **************************** - **************************** - ''' - - - - ''' - **************************** - GENERIC GET & SET commands - **************************** - ''' - - - ''' - **************************** - GENERIC DO commands - **************************** - ''' - - diff --git a/src/devices_controller/devices_controller_abstract_component/device_controller_filter_selector.py b/src/devices_controller/devices_controller_abstract_component/device_controller_filter_selector.py deleted file mode 100755 index 8d90fd0..0000000 --- a/src/devices_controller/devices_controller_abstract_component/device_controller_filter_selector.py +++ /dev/null @@ -1,78 +0,0 @@ -#!/usr/bin/env python3 - -""" -Filter selector (abstract) implementation - -To be used as a base class (interface) for any concrete Filter selector class -""" - - -import sys - -# from sockets_tele/ -sys.path.append("..") -# from src_socket/client/ -#import src.core.pyros_django.utils.celme as celme - -sys.path.append("../../..") -import src.core.celme as celme - - -# Local application imports -#from devices_controller.devices_controller_abstract_component.device_controller_abstract import * -sys.path.append("../..") -from devices_controller.devices_controller_abstract_component.device_controller_abstract import * - - -# Default timeouts -TIMEOUT_SEND = 10 -TIMEOUT_RECEIVE = 10 - - - -#class SocketClientTelescopeAbstract(SocketClientAbstract): -class DeviceControllerFilterSelector(DeviceControllerAbstract): - - # @abstract (to be overriden) - _cmd_device_concrete = {} - _cmd_device_abstract = {} - - - #TODO: remplacer PROTOCOL par "SOCKET-TCP", "SOCKET-UDP", "SERIAL", ou "USB" - def __init__(self, device_host:str="localhost", device_port:int=11110, PROTOCOL:str="TCP", buffer_size=1024, DEBUG=False): - ''' - :param device_host: server IP or hostname - :param device_port: server port - :param PROTOCOL: "UDP" or "TCP" - ''' - super().__init__(device_host, device_port, PROTOCOL, buffer_size, DEBUG) - # overwrite abstract _cmd dictionary with subclass native _cmd_native dictionary: - #self._cmd = {**self._cmd, **self._cmd_native} - - - - - ''' - **************************** - **************************** - GENERIC TELESCOPE COMMANDS (abstract methods) - **************************** - **************************** - ''' - - - - ''' - **************************** - GENERIC GET & SET commands - **************************** - ''' - - - ''' - **************************** - GENERIC DO commands - **************************** - ''' - - diff --git a/src/devices_controller/devices_controller_abstract_component/device_controller_plc.py b/src/devices_controller/devices_controller_abstract_component/device_controller_plc.py deleted file mode 100755 index 4ec5bbd..0000000 --- a/src/devices_controller/devices_controller_abstract_component/device_controller_plc.py +++ /dev/null @@ -1,133 +0,0 @@ -#!/usr/bin/env python3 - -"""Socket Client Telescope (abstract) implementation - -To be used as a base class (interface) for any concrete socket client telescope class -""" - - -# Standard library imports -#from enum import Enum -import functools -import logging -import socket -import sys -import time - -# Third party imports - -# from sockets_tele/ -sys.path.append("..") -# from src_socket/client/ -sys.path.append("../../..") -#import src.core.pyros_django.utils.celme as celme -import src.core.celme as celme - - -# Local application imports -#sys.path.append('../..') -#from src.client.socket_client_abstract import UnknownCommandException, SocketClientAbstract -from src_device.client.device_controller_abstract import * - - - - -# Execute also "set" and "do" commands -GET_ONLY=False -# Execute only "get" commands -#GET_ONLY=True - -# Default timeouts -TIMEOUT_SEND = 10 -TIMEOUT_RECEIVE = 10 - - -''' -class c(Enum): - - # GET, SET - DEC = 'DEC' - RA = 'RA' - RA_DEC = 'RA_DEC' - - # DO - PARK = 'PARK' - WARM_START = 'WARM_START' -''' - - - - - -class DeviceControllerPLC(DeviceControllerAbstract): - - # @abstract (to be overriden) - _cmd_native = {} - _cmd = { - # GET-SET commands: - 'get_ack': [], - - 'get_timezone': [], - 'set_timezone': [], - - 'get_date': [], - 'set_date': [], - - 'get_time': [], - 'set_time': [], - - # DO commands: - } - - - - def __init__(self, server_host:str="localhost", server_port:int=11110, PROTOCOL:str="TCP", buffer_size=1024, DEBUG=False): - ''' - :param server_host: server IP or hostname - :param server_port: server port - :param PROTOCOL: "UDP" or "TCP" - ''' - super().__init__(server_host, server_port, PROTOCOL, buffer_size, DEBUG) - # overwrite abstract _cmd dictionary with subclass native _cmd_native dictionary: - self._cmd = {**self._cmd, **self._cmd_native} - - - - - - - ''' - **************************** - **************************** - GENERIC PLC COMMANDS (abstract methods) - **************************** - **************************** - ''' - - - - ''' - **************************** - GENERIC GET & SET commands - **************************** - ''' - - # @abstract - @generic_cmd - def get_ack(self): pass - #return self.execute_generic_cmd("get_ack") - - - - ''' - **************************** - GENERIC DO commands - **************************** - ''' - - - - -# TODO: empecher de creer une instance de cette classe abstraite -# Avec ABC ? - diff --git a/src/devices_controller/devices_controller_abstract_component/device_controller_telescope.py b/src/devices_controller/devices_controller_abstract_component/device_controller_telescope.py deleted file mode 100755 index 4b948be..0000000 --- a/src/devices_controller/devices_controller_abstract_component/device_controller_telescope.py +++ /dev/null @@ -1,569 +0,0 @@ -#!/usr/bin/env python3 - -"""Socket Client Telescope (abstract) implementation - -To be used as a base class (interface) for any concrete socket client telescope class -""" - - -# Standard library imports -#from enum import Enum -import functools -import logging -import socket -import sys -import time - -# Third party imports - -# from sockets_tele/ -sys.path.append("..") -# from src_socket/client/ -sys.path.append("../../../..") -#import src.core.pyros_django.utils.celme as celme -import src.core.celme as celme - - -# Local application imports -#sys.path.append('../..') -#from src.client.socket_client_abstract import UnknownCommandException, SocketClientAbstract -##from src_socket.client.socket_client_abstract import * -from devices_controller.devices_controller_abstract_component.device_controller_abstract import * - - - - -# Execute also "set" and "do" commands -#GET_ONLY=False -# Execute only "get" commands -#GET_ONLY=True - -# Default timeouts -TIMEOUT_SEND = 10 -TIMEOUT_RECEIVE = 10 - - -''' -class c(Enum): - - # GET, SET - DEC = 'DEC' - RA = 'RA' - RA_DEC = 'RA_DEC' - - # DO - PARK = 'PARK' - WARM_START = 'WARM_START' -''' - - - - -class Position(): - x = 0 - y = 0 - def __init__(self, x,y): - self.x = x - self.y = y - def get_values(self): - return self.x, self.y - - - -#class SocketClientTelescopeAbstract(SocketClientAbstract): -#class TelescopeControllerAbstract(DeviceControllerAbstract): -class DeviceControllerTelescope(DeviceControllerAbstract): - - # @abstract (to be overriden) - _cmd_device_concrete = {} - _cmd_device_abstract = { - # GET-SET commands: - 'get_ack': [], - - 'get_ra': [], - 'set_ra': [], - 'get_dec': [], - 'set_dec': [], - 'get_radec': ['get_radec'], - 'set_radec': ['set_radec'], - - ''' - 'get_timezone': [], - 'set_timezone': [], - - 'get_date': [], - 'set_date': [], - - 'get_time': [], - 'set_time': [], - ''' - - 'get_longitude': [], - 'set_longitude': [], - 'get_latitude': [], - 'set_latitude': [], - - 'get_velocity': [], - 'set_velocity': [], - - # DO commands: - ##'do_init': ['do_init'], - ##'do_park': [], - 'do_goto': [], - 'do_move': [], - 'do_movenorth': [], - 'do_movesouth': [], - 'do_movewest': [], - 'do_moveeast': [], - 'do_move_dir': [], - 'do_warm_start': [], - 'do_prec_refr': [], - } - - - #TODO: remplacer PROTOCOL par "SOCKET-TCP", "SOCKET-UDP", "SERIAL", ou "USB" - def __init__(self, device_host:str="localhost", device_port:int=11110, PROTOCOL:str="TCP", buffer_size=1024, DEBUG=False): - ''' - :param device_host: server IP or hostname - :param device_port: server port - :param PROTOCOL: "UDP" or "TCP" - ''' - super().__init__(device_host, device_port, PROTOCOL, buffer_size, DEBUG) - # overwrite abstract _cmd dictionary with subclass native _cmd_native dictionary: - #self._cmd = {**self._cmd, **self._cmd_native} - - - - - def get_celme_longitude(self, longitude): - return celme.Angle(longitude).sexagesimal("d:+0180.0") - def get_celme_latitude(self, latitude): - return celme.Angle(latitude).sexagesimal("d:+090.0") - - - - ''' - TELESCOPE COMMANDS (abstract methods) - ''' - - - - ''' - **************************** - **************************** - GENERIC TELESCOPE COMMANDS (abstract methods) - **************************** - **************************** - ''' - - - - ''' - **************************** - GENERIC GET & SET commands - **************************** - ''' - - # @abstract - @generic_cmd - def get_ack(self): pass - #return self.execute_generic_cmd("get_ack") - - # RA/DEC - # @abstract - ''' - Sets the object's Right Ascension and the object status to "Not Selected". - The :Sd# command has to follow to complete the selection. - The subsequent use of the :ON...# command is recommended (p106) - :Sr:.# - or - :Sr::# - 0 if invalid - 1 if valid - ''' - @generic_cmd - def get_ra(self): pass - #def get_ra(self): return self.execute_generic_cmd("get_ra") - @generic_cmd - def set_ra(self, ra): pass - #return self._set("ra", ra) - - ''' - Sets the object's declination. - It is important that the :Sr# command has been send prior. - Internal calculations are done that may take up to 0.5 seconds. - If the coordinate selection is valid the object status is set to "Selected" - :Sd{+-}
{*°}# - or - :Sd{+- }
{*°:}: - 0 if invalid - 1 if valid - ''' - @generic_cmd - def get_dec(self): pass - #def get_dec(self): return self.execute_generic_cmd("get_dec") - @generic_cmd - def set_dec(self, dec): pass - #def set_dec(self, dec): return self._set("dec", dec) - - # MACRO radec - #def get_radec(self): return self._get("RADEC") - #def get_radec(self)->tuple: return ((self.get_ra()), (self.get_dec())) - def get_radec(self)->GenericResult: - return GenericResult(self.get_ra().txt + "," + self.get_dec().txt) - - # MACRO - def set_radec(self, ra, dec)->GenericResult: - self.set_ra(ra) - self.set_dec(dec) - return GenericResult("OK") - - @generic_cmd - def get_long(self): pass - @generic_cmd - def set_long(self, longitude): pass - - @generic_cmd - def get_lat(self): pass - @generic_cmd - def set_lat(self, latitude): pass - - @generic_cmd - def get_vel(self): pass - - - - - ''' - **************************** - GENERIC DO commands - **************************** - ''' - - # @abstract - #def do_INIT(self): return self._do("INIT") - - ''' do_PARK() (p103) - - STARTUP position = CWD - - :hC# - - position required for a Cold or Warm Start, pointing to the celestial pole of the given hemisphere (north or south), - with the counterweight pointing downwards (CWD position). From L4, V1.0 up - - HOME position parking => par defaut, c'est CWD, mais ca peut etre different - - :hP# - - defaults to the celestial pole visible at the given hemisphere (north or south) and can be set by the user - ''' - - @generic_cmd - def do_move(self): pass - @generic_cmd - def do_movenorth(self): pass - @generic_cmd - def do_movesouth(self): pass - @generic_cmd - def do_movewest(self): pass - @generic_cmd - def do_moveeast(self): pass - - # @abstract - #def do_GOTO(self, pos:Position): return self._do("GOTO") - #def do_WARM_START(self): return self._do("WARM_START") - @generic_cmd - def do_warm_start(self): pass - - @generic_cmd - def do_prec_refr(self): pass - - - - # MACRO generic command - def do_init(self): - - ''' - 1) Send cde ACK ('06') and check answer to see if telescope is ready (see doc page 100) - (utile pour savoir si tout est ok ; par ex, si une raquette est branchée sur le tele, ça peut bloquer le protocole) - Usable for testing the serial link and determining the type of mount (German equatorial). - Return code can be: - - B# while the initial startup message is being displayed (new in L4), - - b# while waiting for the selection of the Startup Mode, - - S# during a Cold Start (new in L4), - - G# after completed startup ==> MEANS ALL IS OK - ''' - #ACK = self.get("ACK") - ACK = self.get_ack() - - ''' - 2) IF telescope is not ready (still starting up), ask it to do a Warm Start ('bW#') - During Startup, with a "b#" being returned, the PC can select the startup mode by sending a - • bC# for selecting the Cold Start, - • bW# for selecting the Warm Start, - • bR# for selecting the Warm Restart - If not ok (still starting up, no 'G#' in return), send 'bW#' (see above) for selecting the Warm Start - ''' - #if ACK != 'G': - if not ACK.ok: - self.do_warm_start() - ACK = self.get_ack() - elapsed_time = 0 - while not ACK.ok: - time.sleep(1) - elapsed_time += 1 - if elapsed_time == TIMEOUT_RECEIVE: raise TimeoutException() - ACK = self.get_ack() - - - ''' - 3) Set timezone, date, and time (p109) - ''' - - ''' - a) set TIMEZONE - Set the number of hours by which your local time differs from UTC. - If your local time is earlier than UTC set a positive value, - if later than UTC set a negative value. The time difference has to be set before setting the calendar date (SC) and local time (SL), since the Real Time Clock is running at UTC - => :SG{+-}hh# - ''' - res = self.get_timezone() - print("Current timezone is", res) - res = self.set_timezone('+00') - #if res != '1': raise UnexpectedCommandReturnCode(res) - if not res.ok: raise UnexpectedCommandReturnCode(res) - res = self.get_timezone() - if res.txt != '+00': raise UnexpectedCommandReturnCode(res) - print("NEW timezone set is", res) - - - ''' - b) set DATE - Set Calendar Date: - months mm, days dd, year yy of the civil time according to the timezone set. - The internal calendar/clock uses GMT - :SC/
/# - 0 if invalid - or - TODO: - 1Updating planetary data#<24 blanks># - ''' - res = self.get_date() - print("Current date is", res) - # format is 2018-09-26T17:50:21 - d = self.get_utc_date() - # format to mm/dd/yy - now_utc_mm_dd_yy = d[5:7] + '/' + d[8:10] + '/' + d[2:4] - #print("date is", now_utc_mm_dd_yy) - res = self.set_date(now_utc_mm_dd_yy) - #res = self.set_DATE(self.get_utc_date()) - #if res[0] != '1': raise UnexpectedCommandReturnCode(res) - #if not res.startswith('1Updating planetary data'): raise UnexpectedCommandReturnCode(res) - if not res.ok: raise UnexpectedCommandReturnCode(res) - res = self.get_date() - if res.txt != now_utc_mm_dd_yy: raise UnexpectedCommandReturnCode(res) - print("NEW DATE set is", res) - - ''' - c) set TIME - Set RTC Time from the civil time hours hh, minutes mm and seconds ss. - The timezone must be set before using this command - :SL::# - ''' - res = self.get_time() - print("Current time is", res) - _,now_utc_hh_mm_ss = d.split('T') - #print("time is", now_utc_hh_mm_ss[:5]) - res = self.set_time(now_utc_hh_mm_ss) - #if res != '1': raise UnexpectedCommandReturnCode(res) - if not res.ok: raise UnexpectedCommandReturnCode(res) - res = self.get_time() - if res.txt[:5] != now_utc_hh_mm_ss[:5]: raise UnexpectedCommandReturnCode(res) - print("NEW TIME set is", res) - - - ''' - 4) Set LOCATION (lat,long) (p103,110) - Pour l'observatoire de Guitalens: - Sg = 2.0375 E - St = 43.6443 N - (attention, 2.0375 E = - 2.0375) - ''' - - ''' - a) set Longitude - Sets the longitude of the observing site to ddd degrees and mm minutes. - The longitude has to be specified positively for western latitudes - (west of Greenwich, the plus sign may be omitted) and negatively for eastern longitudes. - Alternatively, 360 degrees may be added to eastern longitudes. - => :Sg{+-}*# - ''' - # TELE format is -002°02 (I convert it to -002:02) - res = self.get_long() - print("Current longitude is", res) - - # CELME format is -002:02:15 - res = self.get_celme_longitude("-2.0375") - res_ddd_mm = res[:-3] - #res_ddd_mm = res[:-3].replace(':','*') - #res_ddd_mm = '-002:03' - - #print("celme longitude is", res) - ddd,mm,ss = res.split(':') - #dddmm = '-002*03' - res = self.set_long(ddd+'*'+mm) - #if res != '1': raise UnexpectedCommandReturnCode(res) - if not res.ok: raise UnexpectedCommandReturnCode(res) - res = self.get_long() - if res.txt != res_ddd_mm: raise UnexpectedCommandReturnCode(res_ddd_mm, res.txt) - print("NEW longitude set is", res) - - ''' - b) set Latitude - Sets the latitude of the observing site to dd degrees, mm minutes. - The minus sign indicates southern latitudes, the positive sign may be omitted. - => :St{+-}
*# - ''' - # TELE format is +43°38 (I convert it to +43:38) - res = self.get_lat() - print("Current latitude is", res) - - # CELME format is +43:38:15 - res = self.get_celme_latitude("+43.6443") - res_dd_mm = res[:-3] - #res_dd_mm = res[:-3].replace(':','*') - print("res is", res) - #res_dd_mm = '+43:50' - - #print("celme longitude is", res) - dd,mm,ss = res.split(':') - ddmm = dd+'*'+mm - #ddmm = '+43*50' - res = self.set_lat(ddmm) - #if res != '1': raise UnexpectedCommandReturnCode(res) - if not res.ok: raise UnexpectedCommandReturnCode(res) - res = self.get_lat() - if res.txt != res_dd_mm: raise UnexpectedCommandReturnCode(res_dd_mm,res.txt) - print("NEW latitude set is", res) - - - ''' - 5) Send cde ':p3#' : Precession & Refraction (see page 107) - Ask Gemini to do Precession calculation - Coordinates transferred to the Gemini refer to the standard epoch J2000.0. - Refraction is calculated (From L4, V1.0 up) - ''' - self.do_prec_refr() - - return GenericResult("OK") - - - # @abstract - def set_speed(self, speed_rate): - pass - - - ''' GOTO (p105) - - GOTO(position, blocking=Y/N): - (MS = move start) - = Goto RA=18h23m45s Dec=+34d00m00s J2000 - - radec.goto() - ''' - # MACRO generic command - def do_goto(self, ra, dec, speed_rate=None): - - # 1) set speed - if speed_rate: self.set_speed(speed_rate) - - radec = self.get_radec() - print("Current position is", radec) - - # 2) set RA-DEC - ''' - :Sr18:23:45#:Sd+34:00:00#:MS# - ''' - res = self.set_ra(ra) - #if res != '1': raise UnexpectedCommandReturnCode(res) - if res.ko: raise UnexpectedCommandReturnCode(res) - res = self.set_dec(dec) - #if res != '1': raise UnexpectedCommandReturnCode(res) - if res.ko: raise UnexpectedCommandReturnCode(res) - - # 3) MOVE (non blocking by default for Gemini) - self.do_move() - - # 4) Test velocity until it is "Tracking" - ''' - After MOVE, test velocity with ':Gv#' (p103) : we should have 'S', then 'C', then 'T' - - N (for "no tracking") - - T (for Tracking) - - G (for Guiding) - - C (for Centering) - - S (for Slewing) - ''' - vel = None - while vel != 'T': - v = self.get_vel() - vel = v.txt - print("Velocity is", v) - time.sleep(2) - - time.sleep(2) - radec= self.get_radec() - print("Current position is", radec) - - return GenericResult("OK") - - - # @abstract MACRO - def do_move_dir(self, dir, nbsec, speed_rate=None): - dir = dir.upper() - if speed_rate: self.set_speed(speed_rate) - if dir=="NORTH": self.do_movenorth() - elif dir=="SOUTH": self.do_movesouth() - elif dir=="WEST": self.do_movewest() - elif dir=="EAST": self.do_moveeast() - else: raise UnknownCommandException(dir) - time.sleep(int(nbsec)) - self.do_stop() - return GenericResult("OK") - - - - - - -# TODO: empecher de creer une instance de cette classe abstraite -# Avec ABC ? - -''' -if __name__ == "__main__": - - #HOST, PORT = "localhost", 9999 - #HOST, PORT = "localhost", 20001 - HOST, PORT = "localhost", 11110 - - # Classic usage: - #tsock = SocketClient_UDP_TCP(HOST, PORT, "UDP") - # More elegant usage, using "with": - with SocketClient_ABSTRACT(HOST, PORT, "UDP") as tsock: - - # 0) CONNECT to server (only for TCP, does nothing for UDP) - tsock._connect_to_server() - - while True: - - # 1) SEND REQUEST data to server - # saisie de la requête au clavier et suppression des espaces des 2 côtés - data = input("REQUEST TO SERVER [ex: ':GD#' (Get Dec), ':GR#' (Get RA)']: ").strip() - # test d'arrêt - if data=="": break - #data_to_send = bytes(data + "\n", "utf-8") - tsock.send_data(data) - #mysock.sendto("%s" % data, (HOST, PORT)) - #print("Sent: {}".format(data)) - - # 2) RECEIVE REPLY data from server - data_received = tsock.receive_data() - #reponse, adr = mysock.recvfrom(buf) - #print("Received: {}".format(data_received)) - #print("Useful data received: {}".format(data_useful)) - print('\n') - - #tsock.close() -''' \ No newline at end of file diff --git a/src/devices_controller/devices_controller_concrete/device_controller_AK/.emptyfile b/src/devices_controller/devices_controller_concrete/device_controller_AK/.emptyfile deleted file mode 100644 index e69de29..0000000 --- a/src/devices_controller/devices_controller_concrete/device_controller_AK/.emptyfile +++ /dev/null diff --git a/src/devices_controller/devices_controller_concrete/device_controller_AK/device_controller_plc_ak.py b/src/devices_controller/devices_controller_concrete/device_controller_AK/device_controller_plc_ak.py deleted file mode 100755 index 706424f..0000000 --- a/src/devices_controller/devices_controller_concrete/device_controller_AK/device_controller_plc_ak.py +++ /dev/null @@ -1,259 +0,0 @@ -#!/usr/bin/env python3 - -"""Socket Client Gemini Telescope implementation -To be used as a concrete class to system control a Gemini telescope -""" - -# Standard library imports -#import socket -#import logging -import sys -import time - -# Third party imports -# None - -# Local application imports -sys.path.append('../..') -#from src.client.socket_client_telescope_abstract import Position, UnknownCommandException, TimeoutException, SocketClientTelescopeAbstract -from src_device.client.plc_controller_abstract import * - -# Default timeouts -TIMEOUT_SEND = 10 -TIMEOUT_RECEIVE = 10 - -# COMMON CONSTANTS WITH SERVER -TERMINATOR = '\x00' -COMMAND5 = '050000' -#COMMAND6_SIMPLE = '\x00\x06' -COMMAND6 = '\x00\x06\x00' -COMMAND6_SIMPLE = '6' - - - - -class DeviceControllerPLCAK(DeviceControllerPLC): - - # STAMP : - # 00 00 00 00 00 00 00' (8 bytes from '00' to 'FF', 1st one is LOW BYTE) - # ex: 01 + STAMP = 01 00 00 00 00 00 00 00' (8 bytes from '00' to 'FF', 1st one is LOW BYTE) - #STAMP_FILLER = '00000000000000' - #MY_FULL_STAMP = MYSTAMP + STAMP_FILLER - - # Initialize request number (will be increased at each request) - request_num = 0 - # Request number stands on 4 digits (from 0001 to 9999) - request_num_nb_digits = 4 - # For 4 digits, we should get the format '{:04d}': - request_num_format = '{:0'+str(request_num_nb_digits)+'d}' - # Stamp filler with 0 - STAMP_FILLER = '0' * (8 - request_num_nb_digits) - - #data = " ".join(sys.argv[1:]) - #data_to_send = bytes(data + "\n", "utf-8") - - ''' Commands dictionary - NEW foramt is: - 'generic-command-name': ['native-command-name', 'awaited_result_if_ok', 'other-awaited-results if not ok...], - - Old format was: - 'CMD-NAME': ['cmd-get', 'awaited_res_ok_for_get', 'cmd-set', 'awaited_res_ok_for_set', 'comment'], - ''' - # @overwrite - _cmd_native = { - # GET @ SET commands - 'get_ack': [COMMAND6, 'G', 'B','b','S'], # B# while the initial startup message is being displayed (new in L4), b# while waiting for the selection of the Startup Mode, S# during a Cold Start (new in L4), G# after completed startup. - - # RA-DEC (p109-110) - #:Sr:.# or :Sr::# - #Sets the object's Right Ascension and the object status to "Not Selected". The :Sd# command has to follow to complete the selection. The subsequent use of the :ON...# command is recommended - #:Sd{+-}
{*°}# or :Sd{+- }
{*°:}: - #Sets the object's declination. It is important that the :Sr# command has been send prior. Internal calculations are done that may take up to 0.5 seconds. If the coordinate selection is valid the object status is set to "Selected" - #'RADEC': [('GR','GD'), ''], - #'ra': ['GR', None, 'Sr', None, 'commentaire'], - 'get_ra': ['GR'], - 'set_ra': ['Sr'], - 'get_dec': ['GD'], - 'set_dec': ['Sd'], - # get_radec and set_radec are already defined in abstract class - - 'get_timezone': ['GG'], - 'set_timezone': ['SG', '1'], - - # ALT-AZ (p?) - "get_alt": ['GA'], - "get_az": ['GZ'], - #"ALT-AZ": [('GA','GZ'), ''], - - # LONG-LAT - "get_long": ['Gg'], - "set_long": ['Sg'], - "get_lat": ['Gt'], - "set_lat": ['St'], - #"LONGLAT": [('Gg','Gt'), ''], - - "get_hangle": ['GH'], - - 'get_vel': ['Gv'], - #"get_maxvel": ['Gv'], - - 'get_date': ['GC'], - 'set_date': ['SC'], - 'get_time': ['GL'], - 'set_time': ['SL'], - - # DO commands - # defined in abstract class: - #'do_init': ['do_init'], - 'do_park': ['hP'], - 'do_warm_start': ['bW'], - 'do_prec_refr': ['p3'], - 'do_move': ['MS', '0', '1Object below horizon.', '2No object selected.', '3Manual Control.', '4Position unreachable.', '5Not aligned.', '6Outside Limits.' ], - 'do_movenorth': ['Mn'], - 'do_movesouth': ['Ms'], - 'do_movewest': ['Mw'], - 'do_moveeast': ['Me'], - 'do_stop': ['Q'], - } - - - # @overwrite - # Gemini is using UDP - def __init__(self, server_host:str="localhost", server_port:int=11110, DEBUG=False): - super().__init__(server_host, server_port, "TCP", 1024, DEBUG) - - - # @overwrite - def formated_cmd(self, cmd:str, values_to_set:str=None)->str: - if values_to_set != None: - for value_to_set in values_to_set: - cmd += value_to_set - if cmd not in (COMMAND6, COMMAND5): - cmd += '#' - if cmd not in ('bC#','bW#','bR#'): - cmd=':'+cmd - return cmd - - - # @overwrite - def encapsulate_data_to_send(self, command:str): - r''' Encapsulate useful data to be ready for sending - - If data is "complete" (with stamp, and well formatted), send it as is - Otherwise, add stamp and format it - - 3 types of commands: - - TYPE1: '06' or '050000' - - TYPE2: ':cde#' (mainly ':??#' - i.e : ':GD#', ':GR#', ...) - - TYPE3: 'b?#' (bC# = Cold Start, bW# = selecting Warm Start, bR# = selecting Warm Restart) - - :Examples: - >>> tele = SocketClientTelescopeGemini("localhost", 11110, DEBUG=False) - >>> tele.encapsulate_data_to_send(':GD#') - '00010000:GD#\x00' - >>> tele.encapsulate_data_to_send(':GR#') - '00020000:GR#\x00' - >>> tele.close() - - # ne marche pas => '00010000:GD#\x00' - ''' - - ####return bytes(self.MYSTAMP + self.STAMP_FILLER + data + "\n", "utf-8") - - # TYPE1 - #print("command to encapsulate is", repr(command)) - - if command == COMMAND6_SIMPLE: command = COMMAND6 - - if command not in (COMMAND6, COMMAND5): - # TYPE2 or 3 - #has_starting_car = data.find(':') > -1 - if len(command) < 3: raise UnknownCommandException() - if not (command[-1]=='#'): raise UnknownCommandException() - if not (command[0]==':') and command not in ('bC#','bW#','bR#'): raise UnknownCommandException() - #stamp,command = data.split(':',1) - - #if command.find('#')>-1: command,_ = command.split('#',1) - self.request_num += 1 - # Format to request_num_nb_digits (4) digits - request_num_str = self.request_num_format.format(self.request_num) - self.last_stamp = request_num_str + self.STAMP_FILLER - - #if command == COMMAND6_SIMPLE: command = COMMAND6_REAL - return self.last_stamp + command + TERMINATOR - #return bytes(data + "\n", "utf-8") - ####return bytes(self.MYSTAMP + self.STAMP_FILLER + data + "\n", "utf-8") - - # @overwrite - def uncap_received_data(self, data_received_bytes:bytes)->str: - r""" - Extract useful data from received raw data - - >>> tele = SocketClientTelescopeGemini("localhost", 11110, DEBUG=False) - >>> tele.last_stamp = '00170000' - >>> tele.uncap_received_data(b'001700001\x00') - '1' - >>> tele.close() - """ - - #print("data_received_bytes type is", type(data_received_bytes)) - #print("data received is", data_received_bytes) - - #TODO: resoudre ce pb plus proprement (utiliser unicode au lieu de utf-8 codec ??? - # BUGFIX: b'\xdf' (should correspond to "°" symbol) generates a UnicodeDecodeError, - # so, replace it by ':' (b'\x3A') - if b'\xdf' in data_received_bytes: - data_received_bytes = data_received_bytes.replace(b'\xdf', b'\x3A') - - data_received = data_received_bytes.decode() - #print("data_received is", data_received) - # Remove STAMP (and \n at the end): - #useful_data = data_received.split(self.MY_FULL_STAMP)[1][:-1] - useful_data = data_received.split(self.last_stamp)[1][:-1] - # Remove '#' at the end, if exists - if useful_data[-1] == '#': useful_data = useful_data[:-1] - return useful_data - - - - -if __name__ == "__main__": - - import doctest - doctest.testmod() - exit() - - # Classic usage: - #plc_client = SocketClient_Gemini(HOST, PORT) - # More elegant usage, using "with": - with DeviceControllerPLCAK("localhost", 11110, DEBUG=False) as plc_client: - - # 0) CONNECT to server (only for TCP, does nothing for UDP) - plc_client._connect_to_server() - - #plc_client.config() - - # Send some commands to the Telescope - #pos = plc_client.get_position() - - # Do EXPERT mode execution - while True: - - # 1) SEND REQUEST data to server - # saisie de la requête au clavier et suppression des espaces des 2 côtés - data = input("(EXPERT MODE) REQUEST TO SERVER [ex: ':GD#' (Get Dec), ':GR#' (Get RA)']: ").strip() - # test d'arrêt - if data == "": break - #data_to_send = bytes(data + "\n", "utf-8") - plc_client.send_data(data) - #mysock.sendto("%s" % data, (HOST, PORT)) - #print("Sent: {}".format(data)) - - # 2) RECEIVE REPLY data from server - data_received = plc_client.receive_data() - #reponse, adr = mysock.recvfrom(buf) - #print("Received: {}".format(data_received)) - #print("Useful data received: {}".format(data_useful)) - print('\n') - - #plc_client.close() \ No newline at end of file diff --git a/src/devices_controller/devices_controller_concrete/device_controller_Gemini/.emptyfile b/src/devices_controller/devices_controller_concrete/device_controller_Gemini/.emptyfile deleted file mode 100644 index e69de29..0000000 --- a/src/devices_controller/devices_controller_concrete/device_controller_Gemini/.emptyfile +++ /dev/null diff --git a/src/devices_controller/devices_controller_concrete/device_controller_Gemini/client_telescope_gemini_controller_run.py b/src/devices_controller/devices_controller_concrete/device_controller_Gemini/client_telescope_gemini_controller_run.py deleted file mode 100755 index eda6ad6..0000000 --- a/src/devices_controller/devices_controller_concrete/device_controller_Gemini/client_telescope_gemini_controller_run.py +++ /dev/null @@ -1,270 +0,0 @@ -#!/usr/bin/env python3 - - -import pprint -import sys - - -''' -(1) -sys.path.append("../../..") -from devices_controller.devices_controller_concrete.device_controller_Gemini.telescope_controller_gemini import TelescopeControllerGemini -ou (2) -#sys.path.append("..") -#from device_controller_Gemini.telescope_controller_gemini import TelescopeControllerGemini -ou (3) -''' -from device_controller_telescope_gemini import DeviceControllerTelescopeGemini - -#DEBUG = False -DEBUG = True - - -""" -class essai: - a = 'a' - c = 'c' - def __init__(self): - self.b = 'bB' - def __get__(self, instance, owner): - return self.b - ''' - def __set__(self, instance, value): - self.b = value - def __str__(self): - return self.b - ''' -class user: - e = essai() - -class GenericResult: - ''' Usage: - res = execute(command) - print("result is", res) - if res.ko: raise UnexpectedReturnCode() - if res.ok: - ... - ''' - # By default, bad result - ok = False - ko = True - - def __init__(self, native_result:str, ok=False): - self.txt = native_result - self.ok = ok - self.ko = not ok - ''' - def __str__(self): - return self.native_result - def __repr__(self): - return self.native_result - def __get__(self, instance, owner): - return self.b - def __set__(self, instance, value): - self.b = value - ''' -""" - - - - -def main(): - - # No argument => connexion to REAL TELESCOPE (DISTANT) - if len(sys.argv) == 1: - # Port local AK 8085 = redirigé sur l’IP du tele 192.168.0.12 sur port 11110 - HOST, PORT = "82.64.28.71", 11110 - - # Args given => connexion to LOCAL simulator server - else: - HOST, PORT = "localhost", 11110 - - #tele_ctrl = SocketClient_UDP_TCP(HOST, PORT) - with DeviceControllerTelescopeGemini(HOST, PORT, DEBUG) as tele_ctrl: - - # (optional) Only useful for TCP (does nothing for UDP) - tele_ctrl._connect_to_device() - - #execute_commands_before_init(tele_ctrl) - - # Initialize telescope config (ack, date, time) - #tele_ctrl.do_init() - - #execute_commands_after_init(tele_ctrl) - - #_mes_tests_temporaires_avirer(tele_ctrl) - - # Do MANUAL mode execution - while True: - - tele_ctrl.print_available_commands() - #req = input("\n(EXPERT MODE) REQUEST TO SERVER [ex: '6' (ACK), ':GD#' (Get Dec), ':GR#' (Get RA)'], (ENTER to quit): ").strip() - cmd = input("REQUEST TO SERVER (ENTER to quit): >>> ").strip() - if not cmd: break - - res = tele_ctrl.execute_cmd(cmd) - print("result is", str(res)) - if res.ok: print("OK") - #print("result.txt is", res.txt) - - # END: park telescope - ###tele_ctrl.do_PARK() - - #tele_ctrl.close() - - - -def execute_commands_before_init(tele_ctrl): - print("RA is", tele_ctrl.get_ra()) - - ack = tele_ctrl.get_ack() - print("ack is", ack) - print("ack.txt is", ack) - if ack.ok: print("ack is ok") - if ack.ko: print("ack is ko") - - -def execute_commands_after_init(tele_ctrl): - - #radec = tele_ctrl.get("RA-DEC") - res = tele_ctrl.get_radec() - print("RA-DEC is", res) - - ''' - GOTO and MOVE: - ------------- - ''' - ''' - tele_ctrl.do_goto(ra='21:00:00',dec='+20:00:00') - tele_ctrl.do_goto(ra='22:00:00',dec='+30:00:00') - tele_ctrl.do_move_dir('east','4', 'slew') - ''' - - # In order to execute a Gemini (non generic) command: - res = tele_ctrl.execute_native_cmd(':GR#') - print("res is", res) - - -# @private -def _mes_tests_temporaires_avirer(tele_client): - - print("\n...Execution de mes tests temporaires...\n") - - print("res is", tele_client.run_func('formated_cmd','GD')) - print("dict is") - pprint.pprint(tele_client._cmd) - - ''' - u = user() - print(u.e) - - res = GenericResult('G',False) - print("res is", res) - if res.ok: print("res is ok") - if not res.ko: print("res is not ko") - if res.txt == 'G': print("res is G") - ''' - - tele_client.do_move_dir('EAST','4', 'slew') - tele_client.do_goto(ra='21:00:00',dec='+20:00:00') - ''' - TEST ENVOI DIRECT: - - #data = 0x01000000:06# - data = b'01000000:06#' - - ##data = b'01000000:GD#' - #data = 0x010000003a474423 - #data = b'010000003a474423' - - #data_to_send = tele_client._format_data_to_send(data) - tele_client._send_data(data) - #data_received = tele_client.receive_data() - data_received = tele_client.mysock.recv(1024) - print("data rec = ", data_received) - ''' - - # Send some commands to the Telescope - #pos = tele_client.get_position() - #dec = tele_client.get("DEC") - #ra = tele_client.get("RA") - ''' - tele_client.get("NONE") - tele_client.set("NONE", 1) - tele_client.do("NONE") - ''' - - # TEST BAS NIVEAU POUR ACK 06: - ''' - >>> a=b'\x3A' - >>> a - b':' - >>> a.decode() - ':' - - >>> a=b'\x47' - >>> a - b'G' - >>> a.decode() - 'G' - - >>> a=b'\x44' - >>> a - b'D' - >>> a.decode() - 'D' - - >>> a=b'\x23' - >>> a - b'#' - >>> a.decode() - '#' - >>> tele_client.mysock.sendto(b'\x00\x00\x00\x01\x00\x00\x00\x00\x3A\x47\x44\x23\x00', (HOST, PORT)) - RECEIVED (ALL BYTES): b'\x00\x00\x00\x01\x00\x00\x00\x00+90.000000#\x00' - - >>> a=b'\x52' - >>> a - b'R' - >>> a.decode() - 'R' - >>> tele_client.mysock.sendto(b'\x00\x00\x00\x01\x00\x00\x00\x00\x3A\x47\x52\x23\x00', (HOST, PORT)) - RECEIVED (ALL BYTES): b'\x00\x00\x00\x01\x00\x00\x00\x008.911844#\x00' - - >>> tele_client.mysock.sendto(b'\x00\x00\x00\x01\x00\x00\x00\x00\x00\x06\x00\x00', (HOST, PORT)) - RECEIVED (ALL BYTES): b'\x00\x00\x00\x01\x00\x00\x00\x00G#\x00' - - ''' - #tele_client.mysock.sendto(b'00030000:GD#\x00', (HOST, PORT)) - #tele_client.mysock.sendto(b'00030000\x06\x00', (HOST, PORT)) - #tele_client.mysock.sendto(b'00030000\x00\x06\x00', (HOST, PORT)) - #tele_client.mysock.sendto(b'00030000\0x06\x00', (HOST, PORT)) - #tele_client.mysock.sendto(b'00010000\0x06\x00', (HOST, PORT)) - ''' - # :GD# - ###tele_client.mysock.sendto(b'\x00\x00\x00\x01\x00\x00\x00\x00\x3A\x47\x44\x23\x00', (HOST, PORT)) - - # :GR# - ###tele_client.mysock.sendto(b'\x00\x00\x00\x01\x00\x00\x00\x00\x3A\x47\x52\x23\x00', (HOST, PORT)) - - # ACK 06 OK !!! : - - tele_client.mysock.sendto(b'\x00\x00\x00\x01\x00\x00\x00\x00\x00\x06\x00\x00', (HOST, PORT)) - RECEIVED (ALL BYTES): b'\x00\x00\x00\x01\x00\x00\x00\x00G#\x00' - - tele_client.mysock.sendto(b'00010000\x00\x06\x00\x00', (HOST, PORT)) - RECEIVED (ALL BYTES): b'00010000G#\x00' - ''' - ''' - # wrong: b'00010000\\x00\\x06\\x00\x00' - # ok: - \x05\x00\x00\ - #tele_client.mysock.sendto(b'\x00\x00\x00\x01\x00\x00\x00\x00\x00\x05\x00\x00\x00\x00\x00', (HOST, PORT)) - #tele_client.mysock.sendto(b'00010000\x00\x05\x00\x00', (HOST, PORT)) - tele_client.mysock.sendto(b'00010000\x00\x06\x00\x00', (HOST, PORT)) - data_received = tele_client.receive_data() - ''' - - - -# Execution -main() diff --git a/src/devices_controller/devices_controller_concrete/device_controller_Gemini/device_controller_telescope_gemini.py b/src/devices_controller/devices_controller_concrete/device_controller_Gemini/device_controller_telescope_gemini.py deleted file mode 100755 index fb2b148..0000000 --- a/src/devices_controller/devices_controller_concrete/device_controller_Gemini/device_controller_telescope_gemini.py +++ /dev/null @@ -1,285 +0,0 @@ -#!/usr/bin/env python3 - -"""Socket Client Gemini Telescope implementation -To be used as a concrete class to system control a Gemini telescope -""" - -# Standard library imports -#import socket -#import logging -import sys -import time - -# Third party imports -# None - -# Local application imports -sys.path.append('../../..') -#from src.client.socket_client_telescope_abstract import Position, UnknownCommandException, TimeoutException, SocketClientTelescopeAbstract -##from src_socket.client.socket_client_telescope_abstract import * -#from devices_controller.devices_controller_abstract_component.telescope_controller_abstract import * -from devices_controller.devices_controller_abstract_component.device_controller_telescope import * - -# Default timeouts -TIMEOUT_SEND = 10 -TIMEOUT_RECEIVE = 10 - -# COMMON CONSTANTS WITH SERVER -TERMINATOR = '\x00' -COMMAND5 = '050000' -#COMMAND6_SIMPLE = '\x00\x06' -COMMAND6 = '\x00\x06\x00' -COMMAND6_SIMPLE = '6' - - - - -##class SocketClientTelescopeGemini(SocketClientTelescopeAbstract): -class DeviceControllerTelescopeGemini(DeviceControllerTelescope): - - # STAMP : - # 00 00 00 00 00 00 00' (8 bytes from '00' to 'FF', 1st one is LOW BYTE) - # ex: 01 + STAMP = 01 00 00 00 00 00 00 00' (8 bytes from '00' to 'FF', 1st one is LOW BYTE) - #STAMP_FILLER = '00000000000000' - #MY_FULL_STAMP = MYSTAMP + STAMP_FILLER - - # Initialize request number (will be increased at each request) - request_num = 0 - # Request number stands on 4 digits (from 0001 to 9999) - request_num_nb_digits = 4 - # For 4 digits, we should get the format '{:04d}': - request_num_format = '{:0'+str(request_num_nb_digits)+'d}' - # Stamp filler with 0 - STAMP_FILLER = '0' * (8 - request_num_nb_digits) - - #data = " ".join(sys.argv[1:]) - #data_to_send = bytes(data + "\n", "utf-8") - - ''' Commands dictionary - NEW foramt is: - 'generic-command-name': ['native-command-name', 'awaited_result_if_ok', 'other-awaited-results if not ok...], - - Old format was: - 'CMD-NAME': ['cmd-get', 'awaited_res_ok_for_get', 'cmd-set', 'awaited_res_ok_for_set', 'comment'], - ''' - # @overwrite - _cmd_device_concrete = { - # GET @ SET commands - 'get_ack': [COMMAND6, 'G', 'B','b','S'], # B# while the initial startup message is being displayed (new in L4), b# while waiting for the selection of the Startup Mode, S# during a Cold Start (new in L4), G# after completed startup. - - # RA-DEC (p109-110) - #:Sr:.# or :Sr::# - #Sets the object's Right Ascension and the object status to "Not Selected". The :Sd# command has to follow to complete the selection. The subsequent use of the :ON...# command is recommended - #:Sd{+-}
{*°}# or :Sd{+- }
{*°:}: - #Sets the object's declination. It is important that the :Sr# command has been send prior. Internal calculations are done that may take up to 0.5 seconds. If the coordinate selection is valid the object status is set to "Selected" - #'RADEC': [('GR','GD'), ''], - #'ra': ['GR', None, 'Sr', None, 'commentaire'], - 'get_ra': ['GR'], - 'set_ra': ['Sr'], - 'get_dec': ['GD'], - 'set_dec': ['Sd'], - # get_radec and set_radec are already defined in abstract class - - 'get_timezone': ['GG'], - 'set_timezone': ['SG', '1'], - - # ALT-AZ (p?) - "get_alt": ['GA'], - "get_az": ['GZ'], - #"ALT-AZ": [('GA','GZ'), ''], - - # LONG-LAT - "get_long": ['Gg'], - "set_long": ['Sg'], - "get_lat": ['Gt'], - "set_lat": ['St'], - #"LONGLAT": [('Gg','Gt'), ''], - - "get_hangle": ['GH'], - - 'get_vel': ['Gv'], - #"get_maxvel": ['Gv'], - - 'get_date': ['GC'], - 'set_date': ['SC'], - 'get_time': ['GL'], - 'set_time': ['SL'], - - # DO commands - # defined in abstract class: - #'do_init': ['do_init'], - 'do_park': ['hP'], - 'do_warm_start': ['bW'], - 'do_prec_refr': ['p3'], - 'do_move': ['MS', '0', '1Object below horizon.', '2No object selected.', '3Manual Control.', '4Position unreachable.', '5Not aligned.', '6Outside Limits.' ], - 'do_movenorth': ['Mn'], - 'do_movesouth': ['Ms'], - 'do_movewest': ['Mw'], - 'do_moveeast': ['Me'], - 'do_stop': ['Q'], - } - - - # @overwrite - # Gemini is using UDP - def __init__(self, device_host:str="localhost", device_port:int=11110, DEBUG=False): - super().__init__(device_host, device_port, "SOCKET-UDP", 1024, DEBUG) - - - # @overwrite - def formated_cmd(self, cmd:str, values_to_set:str=None)->str: - if values_to_set != None: - for value_to_set in values_to_set: - cmd += value_to_set - if cmd not in (COMMAND6, COMMAND5): - cmd += '#' - if cmd not in ('bC#','bW#','bR#'): - cmd=':'+cmd - return cmd - - - - #@override - def encapsulate_data_to_send(self, command:str): - r''' Encapsulate useful data to be ready for sending - - If data is "complete" (with stamp, and well formatted), send it as is - Otherwise, add stamp and format it - - 3 types of commands: - - TYPE1: '06' or '050000' - - TYPE2: ':cde#' (mainly ':??#' - i.e : ':GD#', ':GR#', ...) - - TYPE3: 'b?#' (bC# = Cold Start, bW# = selecting Warm Start, bR# = selecting Warm Restart) - - :Examples: - >>> tele = TelescopeControllerGemini("localhost", 11110, DEBUG=False) - >>> tele.encapsulate_data_to_send(':GD#') - '00010000:GD#\x00' - >>> tele.encapsulate_data_to_send(':GR#') - '00020000:GR#\x00' - >>> tele.close() - - # ne marche pas => '00010000:GD#\x00' - ''' - - ####return bytes(self.MYSTAMP + self.STAMP_FILLER + data + "\n", "utf-8") - - # TYPE1 - #print("command to encapsulate is", repr(command)) - - if command == COMMAND6_SIMPLE: command = COMMAND6 - - if command not in (COMMAND6, COMMAND5): - # TYPE2 or 3 - #has_starting_car = data.find(':') > -1 - if len(command) < 3: raise UnknownCommandException() - if not (command[-1]=='#'): raise UnknownCommandException() - if not (command[0]==':') and command not in ('bC#','bW#','bR#'): raise UnknownCommandException() - #stamp,command = data.split(':',1) - - #if command.find('#')>-1: command,_ = command.split('#',1) - self.request_num += 1 - # Format to request_num_nb_digits (4) digits - request_num_str = self.request_num_format.format(self.request_num) - self.last_stamp = request_num_str + self.STAMP_FILLER - - #if command == COMMAND6_SIMPLE: command = COMMAND6_REAL - data_encapsulated = self.last_stamp + command + TERMINATOR - #return super().encapsulate_data_to_send(data_encapsulated) - #return self.my_channel.encapsulate_data_to_send(data_encapsulated) - return data_encapsulated - - #return bytes(data + "\n", "utf-8") - ####return bytes(self.MYSTAMP + self.STAMP_FILLER + data + "\n", "utf-8") - - # @overwrite - def uncap_received_data(self, data_received:str)->str: - #data_received = super().uncap_received_data(data_received_bytes) - ##data_received = self.my_channel.uncap_received_data(data_received_bytes) - - #>>> tele.uncap_received_data(b'001700001\x00') - - r""" - Extract useful data from received raw data - - >>> tele = TelescopeControllerGemini("localhost", 11110, DEBUG=False) - >>> tele.last_stamp = '00170000' - >>> tele.uncap_received_data('001700001#') - '1' - >>> tele.close() - """ - - print("data_received_bytes type is", type(data_received)) - print("data received is", data_received) - ##data_received = data_received_bytes.decode() - #print("data_received is", data_received) - # Remove STAMP (and \n at the end): - #useful_data = data_received.split(self.MY_FULL_STAMP)[1][:-1] - useful_data = data_received.split(self.last_stamp)[1][:-1] - # Remove '#' at the end, if exists - if useful_data[-1] == '#': useful_data = useful_data[:-1] - return useful_data - - - # @overwrite - #def get_ack(self): return self.execute_native_cmd(COMMAND6, 'G') - #def do_warm_start(self): return self.execute_native_cmd('bW') - - #TODO: déplacer dans parent avec nouvelles commandes set_speed_slew, set_speed_average, ... - # @override - def set_speed(self, speed_rate): - native_cmd = None - # quick - if speed_rate == "slew": native_cmd = 'RS' - # average - if speed_rate == "average": native_cmd = 'RM' - # slow - if speed_rate == "center": native_cmd = 'RC' - # very slow - if speed_rate == "guide": native_cmd = 'RG' - if not native_cmd: raise UnknownCommandException(speed_rate) - return self.execute_unformated_native_cmd(native_cmd) - - -if __name__ == "__main__": - - import doctest - doctest.testmod() - exit() - """ - # Classic usage: - #tele_ctrl = SocketClient_Gemini(HOST, PORT) - # More elegant usage, using "with": - ##with SocketClientTelescopeGemini("localhost", 11110, DEBUG=False) as tele_ctrl: - with TelescopeControllerGemini("localhost", 11110, DEBUG=False) as tele_ctrl: - - # 0) CONNECT to server (only for TCP, does nothing for UDP) - tele_ctrl._connect_to_server() - - #tele_ctrl.config() - - # Send some commands to the Telescope - #pos = tele_ctrl.get_position() - - # Do EXPERT mode execution - while True: - - # 1) SEND REQUEST data to server - # saisie de la requête au clavier et suppression des espaces des 2 côtés - data = input("(EXPERT MODE) REQUEST TO SERVER [ex: ':GD#' (Get Dec), ':GR#' (Get RA)']: ").strip() - # test d'arrêt - if data == "": break - #data_to_send = bytes(data + "\n", "utf-8") - tele_ctrl.send_data(data) - #mysock.sendto("%s" % data, (HOST, PORT)) - #print("Sent: {}".format(data)) - - # 2) RECEIVE REPLY data from server - data_received = tele_ctrl.receive_data() - #reponse, adr = mysock.recvfrom(buf) - #print("Received: {}".format(data_received)) - #print("Useful data received: {}".format(data_useful)) - print('\n') - - #tele_ctrl.close() - """ \ No newline at end of file diff --git a/src/devices_controller/devices_controller_concrete/device_controller_Gemini/device_simulator_telescope_gemini.py b/src/devices_controller/devices_controller_concrete/device_controller_Gemini/device_simulator_telescope_gemini.py deleted file mode 100755 index 3f9e3c4..0000000 --- a/src/devices_controller/devices_controller_concrete/device_controller_Gemini/device_simulator_telescope_gemini.py +++ /dev/null @@ -1,40 +0,0 @@ -#!/usr/bin/env python3 - -import sys - - -#sys.path.append("..") -#sys.path.append("../..") -#sys.path.append("../../..") -#from devices_controller.devices_controller_concrete.device_simulator_common.server_udp_or_tcp import get_SocketServer_UDP_TCP -#from device_simulator_common.server_udp_or_tcp import get_SocketServer_UDP_TCP -from devices_controller.devices_controller_concrete.device_simulator_common.server_udp_or_tcp import get_SocketServer_UDP_TCP -#from server.server_udp_or_tcp import get_SocketServer_UDP_TCP - -HOST = "localhost" - - -# Voir https://stackoverflow.com/questions/10085996/shutdown-socketserver-serve-forever-in-one-thread-python-application - -#class TelescopeGeminiSimulator: -class DeviceSimulatorTelescopeGemini: - #with socketserver_type((HOST, PORT), MyUDPorTCPHandler_classic) as myserver: - - myserver = None - - @classmethod - def serve_forever(cls, PORT): - #with get_SocketServer_UDP_TCP(HOST, PORT, "UDP") as myserver: myserver.serve_forever() - cls.myserver = get_SocketServer_UDP_TCP(HOST, PORT, "UDP") - print("******** myserver: *********", cls.myserver) - try: - cls.myserver.serve_forever() - except KeyboardInterrupt: - pass - cls.myserver.server_close() - cls.myserver.shutdown() - - @classmethod - def stop(cls): - print("******** myserver: *********", cls.myserver) - cls.myserver.shutdown() diff --git a/src/devices_controller/devices_controller_concrete/device_controller_Gemini/server_telescope_gemini_simulator_run.py b/src/devices_controller/devices_controller_concrete/device_controller_Gemini/server_telescope_gemini_simulator_run.py deleted file mode 100755 index 0e4a96f..0000000 --- a/src/devices_controller/devices_controller_concrete/device_controller_Gemini/server_telescope_gemini_simulator_run.py +++ /dev/null @@ -1,18 +0,0 @@ -#!/usr/bin/env python3 - -import sys - - -sys.path.append("..") -#from devices_controller.devices_controller_concrete.device_simulator_common.server_udp_or_tcp import get_SocketServer_UDP_TCP -from device_simulator_common.server_udp_or_tcp import get_SocketServer_UDP_TCP -#from server.server_udp_or_tcp import get_SocketServer_UDP_TCP - -HOST, PORT = "localhost", 11110 -#HOST, PORT = "localhost", 9999 -#HOST, PORT = "localhost", 20001 - -if __name__ == "__main__": - #with socketserver_type((HOST, PORT), MyUDPorTCPHandler_classic) as myserver: - with get_SocketServer_UDP_TCP(HOST, PORT, "UDP") as myserver: - myserver.serve_forever() diff --git a/src/devices_controller/devices_controller_concrete/device_controller_SBIG/device_controller_SBIG.py b/src/devices_controller/devices_controller_concrete/device_controller_SBIG/device_controller_SBIG.py deleted file mode 100755 index 0eba3a0..0000000 --- a/src/devices_controller/devices_controller_concrete/device_controller_SBIG/device_controller_SBIG.py +++ /dev/null @@ -1,300 +0,0 @@ -#!/usr/bin/env python3 - -"""Socket Client Gemini Telescope implementation -To be used as a concrete class to system control a Gemini telescope -""" - -# Standard library imports -#import socket -#import logging -import sys -import time - -# Third party imports -# None - - -# Local application imports - -# devices_controller/ -sys.path.append('../../..') - -# src/ -sys.path.append('../../../..') - -#from src.client.socket_client_telescope_abstract import Position, UnknownCommandException, TimeoutException, SocketClientTelescopeAbstract -##from src_socket.client.socket_client_telescope_abstract import * -#from devices_controller.devices_controller_abstract_component.telescope_controller_abstract import * - -#TODO: Heritage ou plutot COMPOSITION ? -# The SBIG controller has 3 capabilities : filter selector, detector sensor, and detector shutter -#from devices_controller.devices_controller_abstract_component.device_controller_abstract import * -from devices_controller.devices_controller_abstract_component.device_controller_filter_selector import DeviceControllerFilterSelector -from devices_controller.devices_controller_abstract_component.device_controller_detector_sensor import DeviceControllerDetectorSensor -from devices_controller.devices_controller_abstract_component.device_controller_detector_shutter import DeviceControllerDetectorShutter - -# Default timeouts -TIMEOUT_SEND = 10 -TIMEOUT_RECEIVE = 10 - -# COMMON CONSTANTS WITH SERVER -TERMINATOR = '\x00' -COMMAND5 = '050000' -#COMMAND6_SIMPLE = '\x00\x06' -COMMAND6 = '\x00\x06\x00' -COMMAND6_SIMPLE = '6' - - - - -##class SocketClientTelescopeGemini(SocketClientTelescopeAbstract): -#class TelescopeControllerGemini(TelescopeControllerAbstract): -#class DeviceControllerSBIG(DeviceControllerAbstract): -class DeviceControllerSBIG(DeviceControllerDetectorSensor, DeviceControllerDetectorShutter, DeviceControllerFilterSelector): - - # STAMP : - # 00 00 00 00 00 00 00' (8 bytes from '00' to 'FF', 1st one is LOW BYTE) - # ex: 01 + STAMP = 01 00 00 00 00 00 00 00' (8 bytes from '00' to 'FF', 1st one is LOW BYTE) - #STAMP_FILLER = '00000000000000' - #MY_FULL_STAMP = MYSTAMP + STAMP_FILLER - - # Initialize request number (will be increased at each request) - request_num = 0 - # Request number stands on 4 digits (from 0001 to 9999) - request_num_nb_digits = 4 - # For 4 digits, we should get the format '{:04d}': - request_num_format = '{:0'+str(request_num_nb_digits)+'d}' - # Stamp filler with 0 - STAMP_FILLER = '0' * (8 - request_num_nb_digits) - - #data = " ".join(sys.argv[1:]) - #data_to_send = bytes(data + "\n", "utf-8") - - ''' Commands dictionary - NEW foramt is: - 'generic-command-name': ['native-command-name', 'awaited_result_if_ok', 'other-awaited-results if not ok...], - - Old format was: - 'CMD-NAME': ['cmd-get', 'awaited_res_ok_for_get', 'cmd-set', 'awaited_res_ok_for_set', 'comment'], - ''' - # @overwrite - _cmd_device_concrete = { - # GET @ SET commands - 'get_ack': [COMMAND6, 'G', 'B','b','S'], # B# while the initial startup message is being displayed (new in L4), b# while waiting for the selection of the Startup Mode, S# during a Cold Start (new in L4), G# after completed startup. - - # RA-DEC (p109-110) - #:Sr:.# or :Sr::# - #Sets the object's Right Ascension and the object status to "Not Selected". The :Sd# command has to follow to complete the selection. The subsequent use of the :ON...# command is recommended - #:Sd{+-}
{*°}# or :Sd{+- }
{*°:}: - #Sets the object's declination. It is important that the :Sr# command has been send prior. Internal calculations are done that may take up to 0.5 seconds. If the coordinate selection is valid the object status is set to "Selected" - #'RADEC': [('GR','GD'), ''], - #'ra': ['GR', None, 'Sr', None, 'commentaire'], - 'get_ra': ['GR'], - 'set_ra': ['Sr'], - 'get_dec': ['GD'], - 'set_dec': ['Sd'], - # get_radec and set_radec are already defined in abstract class - - 'get_timezone': ['GG'], - 'set_timezone': ['SG', '1'], - - # ALT-AZ (p?) - "get_alt": ['GA'], - "get_az": ['GZ'], - #"ALT-AZ": [('GA','GZ'), ''], - - # LONG-LAT - "get_long": ['Gg'], - "set_long": ['Sg'], - "get_lat": ['Gt'], - "set_lat": ['St'], - #"LONGLAT": [('Gg','Gt'), ''], - - "get_hangle": ['GH'], - - 'get_vel': ['Gv'], - #"get_maxvel": ['Gv'], - - 'get_date': ['GC'], - 'set_date': ['SC'], - 'get_time': ['GL'], - 'set_time': ['SL'], - - # DO commands - # defined in abstract class: - #'do_init': ['do_init'], - 'do_park': ['hP'], - 'do_warm_start': ['bW'], - 'do_prec_refr': ['p3'], - 'do_move': ['MS', '0', '1Object below horizon.', '2No object selected.', '3Manual Control.', '4Position unreachable.', '5Not aligned.', '6Outside Limits.' ], - 'do_movenorth': ['Mn'], - 'do_movesouth': ['Ms'], - 'do_movewest': ['Mw'], - 'do_moveeast': ['Me'], - 'do_stop': ['Q'], - } - - - # @overwrite - # Gemini is using UDP - def __init__(self, device_host:str="localhost", device_port:int=11110, DEBUG=False): - super().__init__(device_host, device_port, "SOCKET-UDP", 1024, DEBUG) - - - # @overwrite - def formated_cmd(self, cmd:str, values_to_set:str=None)->str: - if values_to_set != None: - for value_to_set in values_to_set: - cmd += value_to_set - if cmd not in (COMMAND6, COMMAND5): - cmd += '#' - if cmd not in ('bC#','bW#','bR#'): - cmd=':'+cmd - return cmd - - - - #@override - def encapsulate_data_to_send(self, command:str): - r''' Encapsulate useful data to be ready for sending - - If data is "complete" (with stamp, and well formatted), send it as is - Otherwise, add stamp and format it - - 3 types of commands: - - TYPE1: '06' or '050000' - - TYPE2: ':cde#' (mainly ':??#' - i.e : ':GD#', ':GR#', ...) - - TYPE3: 'b?#' (bC# = Cold Start, bW# = selecting Warm Start, bR# = selecting Warm Restart) - - :Examples: - >>> tele = TelescopeControllerGemini("localhost", 11110, DEBUG=False) - >>> tele.encapsulate_data_to_send(':GD#') - '00010000:GD#\x00' - >>> tele.encapsulate_data_to_send(':GR#') - '00020000:GR#\x00' - >>> tele.close() - - # ne marche pas => '00010000:GD#\x00' - ''' - - ####return bytes(self.MYSTAMP + self.STAMP_FILLER + data + "\n", "utf-8") - - # TYPE1 - #print("command to encapsulate is", repr(command)) - - if command == COMMAND6_SIMPLE: command = COMMAND6 - - if command not in (COMMAND6, COMMAND5): - # TYPE2 or 3 - #has_starting_car = data.find(':') > -1 - if len(command) < 3: raise UnknownCommandException() - if not (command[-1]=='#'): raise UnknownCommandException() - if not (command[0]==':') and command not in ('bC#','bW#','bR#'): raise UnknownCommandException() - #stamp,command = data.split(':',1) - - #if command.find('#')>-1: command,_ = command.split('#',1) - self.request_num += 1 - # Format to request_num_nb_digits (4) digits - request_num_str = self.request_num_format.format(self.request_num) - self.last_stamp = request_num_str + self.STAMP_FILLER - - #if command == COMMAND6_SIMPLE: command = COMMAND6_REAL - data_encapsulated = self.last_stamp + command + TERMINATOR - #return super().encapsulate_data_to_send(data_encapsulated) - #return self.my_channel.encapsulate_data_to_send(data_encapsulated) - return data_encapsulated - - #return bytes(data + "\n", "utf-8") - ####return bytes(self.MYSTAMP + self.STAMP_FILLER + data + "\n", "utf-8") - - # @overwrite - def uncap_received_data(self, data_received:str)->str: - #data_received = super().uncap_received_data(data_received_bytes) - ##data_received = self.my_channel.uncap_received_data(data_received_bytes) - - #>>> tele.uncap_received_data(b'001700001\x00') - - r""" - Extract useful data from received raw data - - >>> tele = TelescopeControllerGemini("localhost", 11110, DEBUG=False) - >>> tele.last_stamp = '00170000' - >>> tele.uncap_received_data('001700001#') - '1' - >>> tele.close() - """ - - print("data_received_bytes type is", type(data_received)) - print("data received is", data_received) - ##data_received = data_received_bytes.decode() - #print("data_received is", data_received) - # Remove STAMP (and \n at the end): - #useful_data = data_received.split(self.MY_FULL_STAMP)[1][:-1] - useful_data = data_received.split(self.last_stamp)[1][:-1] - # Remove '#' at the end, if exists - if useful_data[-1] == '#': useful_data = useful_data[:-1] - return useful_data - - - # @overwrite - #def get_ack(self): return self.execute_native_cmd(COMMAND6, 'G') - #def do_warm_start(self): return self.execute_native_cmd('bW') - - #TODO: déplacer dans parent avec nouvelles commandes set_speed_slew, set_speed_average, ... - # @override - def set_speed(self, speed_rate): - native_cmd = None - # quick - if speed_rate == "slew": native_cmd = 'RS' - # average - if speed_rate == "average": native_cmd = 'RM' - # slow - if speed_rate == "center": native_cmd = 'RC' - # very slow - if speed_rate == "guide": native_cmd = 'RG' - if not native_cmd: raise UnknownCommandException(speed_rate) - return self.execute_unformated_native_cmd(native_cmd) - - -if __name__ == "__main__": - - import doctest - doctest.testmod() - exit() - """ - # Classic usage: - #tele_ctrl = SocketClient_Gemini(HOST, PORT) - # More elegant usage, using "with": - ##with SocketClientTelescopeGemini("localhost", 11110, DEBUG=False) as tele_ctrl: - with TelescopeControllerGemini("localhost", 11110, DEBUG=False) as tele_ctrl: - - # 0) CONNECT to server (only for TCP, does nothing for UDP) - tele_ctrl._connect_to_server() - - #tele_ctrl.config() - - # Send some commands to the Telescope - #pos = tele_ctrl.get_position() - - # Do EXPERT mode execution - while True: - - # 1) SEND REQUEST data to server - # saisie de la requête au clavier et suppression des espaces des 2 côtés - data = input("(EXPERT MODE) REQUEST TO SERVER [ex: ':GD#' (Get Dec), ':GR#' (Get RA)']: ").strip() - # test d'arrêt - if data == "": break - #data_to_send = bytes(data + "\n", "utf-8") - tele_ctrl.send_data(data) - #mysock.sendto("%s" % data, (HOST, PORT)) - #print("Sent: {}".format(data)) - - # 2) RECEIVE REPLY data from server - data_received = tele_ctrl.receive_data() - #reponse, adr = mysock.recvfrom(buf) - #print("Received: {}".format(data_received)) - #print("Useful data received: {}".format(data_useful)) - print('\n') - - #tele_ctrl.close() - """ \ No newline at end of file diff --git a/src/devices_controller/devices_controller_concrete/device_controller_SBIG/device_simulator_SBIG.py b/src/devices_controller/devices_controller_concrete/device_controller_SBIG/device_simulator_SBIG.py deleted file mode 100755 index e7aae00..0000000 --- a/src/devices_controller/devices_controller_concrete/device_controller_SBIG/device_simulator_SBIG.py +++ /dev/null @@ -1,39 +0,0 @@ -#!/usr/bin/env python3 - -import sys - - -#sys.path.append("..") -#sys.path.append("../..") -#sys.path.append("../../..") -#from devices_controller.devices_controller_concrete.device_simulator_common.server_udp_or_tcp import get_SocketServer_UDP_TCP -#from device_simulator_common.server_udp_or_tcp import get_SocketServer_UDP_TCP -from devices_controller.devices_controller_concrete.device_simulator_common.server_udp_or_tcp import get_SocketServer_UDP_TCP -#from server.server_udp_or_tcp import get_SocketServer_UDP_TCP - -HOST = "localhost" - - -# Voir https://stackoverflow.com/questions/10085996/shutdown-socketserver-serve-forever-in-one-thread-python-application - -class DeviceSimulatorSBIG: - #with socketserver_type((HOST, PORT), MyUDPorTCPHandler_classic) as myserver: - - myserver = None - - @classmethod - def serve_forever(cls, PORT): - #with get_SocketServer_UDP_TCP(HOST, PORT, "UDP") as myserver: myserver.serve_forever() - cls.myserver = get_SocketServer_UDP_TCP(HOST, PORT, "UDP") - print("******** myserver: *********", cls.myserver) - try: - cls.myserver.serve_forever() - except KeyboardInterrupt: - pass - cls.myserver.server_close() - cls.myserver.shutdown() - - @classmethod - def stop(cls): - print("******** myserver: *********", cls.myserver) - cls.myserver.shutdown() diff --git a/src/devices_controller/devices_controller_concrete/device_simulator_common/server_udp_or_tcp.py b/src/devices_controller/devices_controller_concrete/device_simulator_common/server_udp_or_tcp.py deleted file mode 100755 index 8de1565..0000000 --- a/src/devices_controller/devices_controller_concrete/device_simulator_common/server_udp_or_tcp.py +++ /dev/null @@ -1,206 +0,0 @@ -#!/usr/bin/env python3 - -# cf https://docs.python.org/3/library/socketserver.html#socketserver-tcpserver-example - -"""Socket Server implementation - -To be used as a minimalist telescope simulator to which a socket client (SocketClient) can connect -""" - -# Standard library imports -import socketserver - -# Third party imports -# None - -# Local application imports -# None - - -class UnknownCommandException(Exception): - pass - - - - -###STAMP = '01000000' -#STAMP = '0100000000000000' -#HOST, PORT = "localhost", 11110 -#HOST, PORT = "localhost", 9999 -#HOST, PORT = "localhost", 20001 - -# stamp is 8 digits long -STAMP_LENGTH = 8 - -# COMMON CONSTANTS WITH CLIENT -TERMINATOR = '\x00' -COMMAND5 = '050000' -#COMMAND6_SIMPLE = '6' -COMMAND6 = '\x00\x06\x00' - - -class Memo: - - @classmethod - def get(cls, var): - return cls._variables[var] - - @classmethod - def set(cls, var, value): - cls._variables[var] = value - - _variables = { - "C" : '10/01/18', - "L" : '10:20:35', - "g" : '+10', - "t" : '+45:00:00', - } - - -def get_SocketServer_UDP_TCP(myhost:str="localhost", myport:int=11110, PROTOCOL:str="TCP"): - socketserver_type = socketserver.UDPServer if PROTOCOL=="UDP" else socketserver.TCPServer - socketserver_handler_type = socketserver.DatagramRequestHandler if PROTOCOL=="UDP" else socketserver.StreamRequestHandler - - """ - A classic version of request handler: - """ - class _MyUDPorTCPHandler_classic(socketserver.BaseRequestHandler): - # The request handler class for our server. - # It is instantiated once per connection to the server, and must - # override the handle() method to implement communication to the client - - def handle(self): - - # Get received data - # - TCP - if PROTOCOL == "TCP": - # For TCP, self.request is the TCP socket connected to the client - data_rcv = self.request.recv(1024).strip() - # - UDP - else: - # For UDP, self.request consists of a pair of data and client socket - data_rcv = self.request[0].strip() - socket_client = self.request[1] - print("{} wrote:".format(self.client_address[0])) - print(data_rcv) - - # Send back the same data, but upper-cased - # - TCP - if PROTOCOL == "TCP": - self.request.sendall(data_rcv.upper()) - # - UDP - # Since there is no connection, the client address must be given explicitly when sending data back via sendto(). - else: - socket_client.sendto(data_rcv.upper(), self.client_address) - - """ - An alternative request handler class that makes use of streams - (file-like objects that simplify communication by providing the standard file interface): - """ - #class MyTCPHandler(socketserver.StreamRequestHandler): - #class MyUDPHandler(socketserver.DatagramRequestHandler): - class _MyUDPorTCPHandler(socketserver_handler_type): - - - def get_useful_data(self, data_received): - return data_received - - def make_answer_for_request(self, request_bytes:bytes): - #if request == STAMP + ":GD#": return STAMP + "+12:28" - #if request == b'0100000000000000:GD#': return bytes(STAMP + "+12:28", "utf-8") - print("Request received is", request_bytes) - - ''' - 3 types of commands: - - TYPE1: '06' or '050000' - - TYPE2: ':cde#' (mainly ':??#' - i.e : ':GD#', ':GR#', ...) - - TYPE3: 'b?#' (bC# = Cold Start, bW# = selecting Warm Start, bR# = selecting Warm Restart) - ''' - - # Convert to string - request = request_bytes.decode("utf-8") - if len(request) < STAMP_LENGTH+2+1: raise UnknownCommandException(request) - - # Remove TERMINATOR - request = request[0:-1] - - # Remove leading stamp - stamp = request[0:STAMP_LENGTH] - command = request[STAMP_LENGTH:] - print("Command received is", repr(command)) - - # TYPE1 - if command not in (COMMAND5, COMMAND6): - # TYPE2 or 3 - if len(command) < 3: raise UnknownCommandException() - if not (command[-1]=='#'): raise UnknownCommandException() - if not (command[0]==':') and command not in ('bC#','bW#','bR#'): raise UnknownCommandException() - - command_start = command[1:3] - if command == COMMAND6: answer = "G" - elif command == COMMAND5: answer = COMMAND5 - - #gr_request = STAMP + ':GR#' + END - #return bytes(stamp + "15:01:48#" + TERMINATOR, "utf-8") - elif command == ':GR#': answer = "15:01:48" - - #gd_request = STAMP + ':GD#' + END - #if request == bytes(gd_request, "utf-8"): return bytes(STAMP + "+12:28#", "utf-8") - #elif useful_request == 'GD': answer = "+12:28" - elif command == ':GD#': answer = "+12:28" - elif command == ':SG+00#': answer = "1" - elif command == ':GG#': answer = "+00" - elif command_start == 'Gv': answer = 'T' - elif command_start in ('GC','GL', 'Gg', 'Gt'): - answer = Memo.get(command_start[1]) - # Gemini telescope replaces "*" with ":" - if command_start in ('Gg','Gt'): answer = answer.replace('*',':') - else: - # Remove ending '#' - command = command[0:-1] - if command_start in ('SC', 'SL', 'Sg', 'St'): Memo.set(command_start[1],command[3:]) - if command[0] == ':': command = command[1:] - answer = command.upper() - - full_answer_in_bytes = bytes(stamp + answer + '#' + TERMINATOR, "utf-8") - #print("request str upper is", str(request).upper()) - print("Answer sent is", full_answer_in_bytes) - return full_answer_in_bytes - - def handle(self): - - # 1) RECEIVE REQUEST from client - # self.rfile is a file-like object created by the handler; - # we can now use e.g. readline() instead of raw recv() calls - data_received = self.rfile.readline().strip() # data is "bytes" type - data_useful = self.get_useful_data(data_received) - #print("data type is", type(data)) - print("\nFrom {}, received: {}".format(self.client_address[0], data_useful)) - - # 2) SEND REPLY to client - # Likewise, self.wfile is a file-like object used to write back to the client - self.wfile.write(self.make_answer_for_request(data_useful)) - # inutile, ca plante car data est deja en bytes: - #self.wfile.write(data.upper().encode()) - - # Return a "classic" handler: - #return socketserver_type((HOST, PORT), _MyUDPorTCPHandler_classic) - - # or a more "modern" handler: - return socketserver_type((myhost, myport), _MyUDPorTCPHandler) - - - - - - - - -if __name__ == "__main__": - #with socketserver_type((HOST, PORT), MyUDPorTCPHandler_classic) as myserver: - ''' - with socketserver_type((HOST, PORT), MyUDPorTCPHandler) as myserver: - myserver.serve_forever() - ''' - with get_SocketServer_UDP_TCP("localhost", 11110, "UDP") as myserver: - myserver.serve_forever() diff --git a/src/devices_controller/doc/Device_controller_class_diag.pu b/src/devices_controller/doc/Device_controller_class_diag.pu deleted file mode 100755 index 2e4bfa2..0000000 --- a/src/devices_controller/doc/Device_controller_class_diag.pu +++ /dev/null @@ -1,153 +0,0 @@ - -@startuml - -/' -UML class Diagram : can be displayed with PlantUML (plugin for Eclipse or for PyCharm) - -PlantUML: -- How to install : https://projects.irap.omp.eu/projects/pyros/wiki/Project_Development#PlantUML -- Eclipse plugin : http://plantuml.com/eclipse -- class diagrams : http://plantuml.com/class-diagram -- sequence diagrams : http://plantuml.com/sequence-diagram -- state diagrams : http://plantuml.com/state-diagram -- Use Case diagrams : http://plantuml.com/use-case-diagram -- OLD Activity diagrams : http://plantuml.com/activity-diagram-legacy -- NEW Activity diagrams : http://plantuml.com/activity-diagram-beta -- Pre-processing (include...) : http://plantuml.com/preprocessing -- GANTT diagrams : http://plantuml.com/gantt-diagram -- REAL WORLD EXAMPLES !!! : https://real-world-plantuml.com/ -- For Python: - - https://github.com/SamuelMarks/python-plantuml - - https://pythonhosted.org/plantuml/ - -UML diagrams theory : https://www.ibm.com/developerworks/rational/library/content/RationalEdge/sep04/bell/index.html -'/ - -title -__**DeviceController and ClientChannel classes diagram (composition)**__ -(ClientChannel is a COMPONENT of DeviceController) -end title - - -/' Channels '/ -ClientChannel <|-- ClientSerial -ClientChannel <|-- ClientSocket -ClientChannel <|-- ClientUSB - -abstract class ClientChannel { - my_channel # socket or serial or usb... - {abstract} connect_to_server() - {abstract} read() - {abstract} put() - put_read() - {abstract} send_data() - {abstract} receive_data() - {abstract} close() -} - -class ClientSocket { - my_channel # (socket) - -- - connect_to_server() - send_data() - receive_data() - close() -} - - -/' Abstract Devices Controllers '/ -DeviceControllerAbstract o-- ClientChannel - -DeviceControllerAbstract <|-- MountPointingControllerAbstract -DeviceControllerAbstract <|-- SecurityManagementControllerAbstract -DeviceControllerAbstract <|-- DetectorSensorControllerAbstract -DeviceControllerAbstract <|-- DetectorShutterControllerAbstract -DeviceControllerAbstract <|-- DetectorFocusControllerAbstract -DeviceControllerAbstract <|-- FilterSelectorControllerAbstract - -abstract class DeviceControllerAbstract { - my_channel # socket or serial or usb... - _cmd = {start, stop, park...} - class GenericResult - -- - {abstract} connect_to_device() - {abstract} _format_data_to_send() - {abstract} _unformat_received_data() - available_commands() - execute() - execute_generic_cmd() - execute_native_cmd() - execute_native_cmd() - send_data() - receive_data() - --- - **Abstract GET/SET/DO commands :** - {abstract} get_timezone(), set_timezone() - {abstract} get_date(), set_date() - {abstract} get_time(), set_time() - {abstract} do_park() - {abstract} do_start() - {abstract} do_stop() - {abstract} do_init() -} - -abstract class MountPointingControllerAbstract { - _cmd = {get_ra, get_dec, do_goto...} - ---- - **Abstract GET/SET/DO commands :** - {abstract} get_ack() - {abstract} get_ra(), set_ra() - {abstract} get_dec(), set_dec() - get_radec(), set_radec() - {abstract} get_lat(), set_lat() - {abstract} get_long(), set_long() - {abstract} set_speed() - {abstract} do_warm_start() - {abstract} do_prec_refr() - ---- - **Generic MACRO commands:** - do_init() - do_goto() - do_move() -} - -abstract class SecurityManagementControllerAbstract { - _cmd = {get_status, set_light...} -} - -abstract class DetectorSensorControllerAbstract { - _cmd = {set_pose, do_start_acq...} -} - -/' Concrete Devices Controllers '/ - -MountPointingControllerAbstract <|-- TelescopeControllerMeade -MountPointingControllerAbstract <|-- TelescopeControllerGemini -MountPointingControllerAbstract <|-- TelescopeControllerColibri - -SecurityManagementControllerAbstract <|-- PLCControllerAK -SecurityManagementControllerAbstract <|-- PLCControllerColibri - -DetectorSensorControllerAbstract <|-- CameraControllerCAGIRE -DetectorSensorControllerAbstract <|-- CameraControllerDDRAGO - -DetectorSensorControllerAbstract <|-- SBIGController -DetectorShutterControllerAbstract <|-- SBIGController -FilterSelectorControllerAbstract <|-- SBIGController - -SBIGController o-> SBIGSimulator -SBIGController <-- AgentDeviceSBIG - -MountPointingControllerAbstract <|-- DeltaTauController -DetectorFocusControllerAbstract <|-- DeltaTauController -FilterSelectorControllerAbstract <|-- DeltaTauController - -class TelescopeControllerGemini { - _cmd = {get_ra, get_dec, do_goto...} - format_data_to_send() - unformat_received_data() -} - -TelescopeControllerMeade : _cmd = {get_ra, get_dec, do_goto...} - -@enduml diff --git a/src/devices_controller/doc/Device_controller_class_diag_multiinheritance_OLD.pu b/src/devices_controller/doc/Device_controller_class_diag_multiinheritance_OLD.pu deleted file mode 100755 index 2cbf361..0000000 --- a/src/devices_controller/doc/Device_controller_class_diag_multiinheritance_OLD.pu +++ /dev/null @@ -1,58 +0,0 @@ - -/' -UML class Diagram : can be displayed with PlantUML (plugin for Eclipse or for PyCharm) - -PlantUML: -- How to install : https://projects.irap.omp.eu/projects/pyros/wiki/Project_Development#PlantUML -- Eclipse plugin : http://plantuml.com/eclipse -- class diagrams : http://plantuml.com/class-diagram -- sequence diagrams : http://plantuml.com/sequence-diagram -- state diagrams : http://plantuml.com/state-diagram -- Use Case diagrams : http://plantuml.com/use-case-diagram -- OLD Activity diagrams : http://plantuml.com/activity-diagram-legacy -- NEW Activity diagrams : http://plantuml.com/activity-diagram-beta -- Pre-processing (include...) : http://plantuml.com/preprocessing -- GANTT diagrams : http://plantuml.com/gantt-diagram -- REAL WORLD EXAMPLES !!! : https://real-world-plantuml.com/ -- For Python: - - https://github.com/SamuelMarks/python-plantuml - - https://pythonhosted.org/plantuml/ - -UML diagrams theory : https://www.ibm.com/developerworks/rational/library/content/RationalEdge/sep04/bell/index.html -'/ - - -@startuml - -title -__**DeviceController and ClientChannel classes diagram (multi-inheritance)**__ -(TelescopeGemini heritates both from DeviceController and ClientChannel) - -end title - - -/' Abstract Devices Controllers '/ -DeviceControllerAbstract <|-- PLCControllerAbstract -DeviceControllerAbstract <|-- CameraControllerAbstract -DeviceControllerAbstract <|-- TelescopeControllerAbstract - -/' Concrete Devices Controllers '/ - -TelescopeControllerAbstract <|-- TelescopeControllerMeade -TelescopeControllerAbstract <|-- TelescopeControllerGemini -ClientSocket <|-- TelescopeControllerGemini -TelescopeControllerAbstract <|-- TelescopeControllerColibri - -PLCControllerAbstract <|-- PLCControllerAK -PLCControllerAbstract <|-- PLCControllerColibri - -CameraControllerAbstract <|-- CameraControllerVIS_AK -CameraControllerAbstract <|-- CameraControllerCAGIRE -CameraControllerAbstract <|-- CameraControllerDDRAGO - -/' Channels '/ -ClientChannel <|-- ClientSocket -ClientChannel <|-- ClientSerial -ClientChannel <|-- ClientUSB - -@enduml diff --git a/src/devices_controller/doc/generate_diagrams.sh b/src/devices_controller/doc/generate_diagrams.sh deleted file mode 100755 index 0ae7c7a..0000000 --- a/src/devices_controller/doc/generate_diagrams.sh +++ /dev/null @@ -1,5 +0,0 @@ -# pip install plantuml -# https://github.com/SamuelMarks/python-plantuml -# https://pythonhosted.org/plantuml/ - -python3 -m plantuml *.pu diff --git a/src/devices_controller/logs.py b/src/devices_controller/logs.py deleted file mode 100755 index efdd126..0000000 --- a/src/devices_controller/logs.py +++ /dev/null @@ -1,77 +0,0 @@ -#!/usr/bin/env python3 - -"""LOGGER - -""" - - -# Standard library imports -import logging - -# Third party imports -# None - -# Local application imports -# None - - - - -logger = None -# Aliases for logger: -def log_d(msg:str): logger.debug(msg) -def log_i(msg:str): logger.info(msg) -def log_w(msg:str): logger.warning(msg) -def log_e(msg:str): logger.error(msg) -def log_c(msg:str): logger.critical(msg) - - - -#def set_logger(self): -def set_logger(DEBUG=False): - global logger - ''' - # Logger configuration - # Log all events, starting from DEBUG level - ''' - - # Basic configuration - ''' - #logging.basicConfig(level=logging.DEBUG) - logging.basicConfig(level=logging.DEBUG, filename='client.log', filemode='a', format='%(asctime)s - %(name)s - %(levelname)s - %(message)s') - logging.debug("Client instanciated") - ''' - - # Advanced configuration - logger = logging.getLogger(__name__) - # Absolument necessaire, sinon rien ne marche !!! - logger.setLevel(logging.DEBUG) - - # Create handlers for writing to console and file - c_handler = logging.StreamHandler() - f_handler = logging.FileHandler('client.log') - - # Set level for each handler - # DEBUG < INFO < WARNING < ERROR < CRITICAL - #if self.DEBUG: - if DEBUG: - c_handler.setLevel(logging.DEBUG) - f_handler.setLevel(logging.DEBUG) - else: - c_handler.setLevel(logging.INFO) - f_handler.setLevel(logging.INFO) - - # Set format for each handler - #c_format = logging.Formatter('%(name)s - %(levelname)s - %(message)s') - c_format = logging.Formatter('%(message)s') - f_format = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') - c_handler.setFormatter(c_format) - f_handler.setFormatter(f_format) - - # Set handlers (for console and file) - logger.addHandler(c_handler) - logger.addHandler(f_handler) - - #return logger - - diff --git a/src/devices_controller/test/.gitignore b/src/devices_controller/test/.gitignore deleted file mode 100644 index e78b574..0000000 --- a/src/devices_controller/test/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/client.log diff --git a/src/devices_controller/test/test_client_gemini.py b/src/devices_controller/test/test_client_gemini.py deleted file mode 100755 index 626911c..0000000 --- a/src/devices_controller/test/test_client_gemini.py +++ /dev/null @@ -1,78 +0,0 @@ -#!/usr/bin/env python3 - -import threading - -import unittest - -import sys -sys.path.append('..') -sys.path.append('../..') -from devices_controller.devices_controller_concrete.device_simulator_common.server_udp_or_tcp import get_SocketServer_UDP_TCP -#from src_socket.client.socket_client_telescope_gemini import SocketClientTelescopeGemini -import devices_controller.devices_controller_concrete.device_controller_Gemini.device_controller_telescope_gemini as gemini - -#HOST, PORT = "localhost", 9999 -#HOST, PORT = "localhost", 20001 -HOST, PORT = "localhost", 11110 - -class TestClient(unittest.TestCase): - - - def setUp(self): - self.seq = range(10) - - def server(self): - with get_SocketServer_UDP_TCP(HOST, PORT, "UDP") as myserver: - myserver.serve_forever() - - - def test_run_doctests(self): - import doctest - doctest.testmod(gemini) - - - def test_run_unittests(self): - - q_a = [ - (':toto#', 'TOTO'), - (':GD#', '+12:28'), - #(':GD#', '+12:28') - ('6', 'G'), - ] - - #TODO: RUN SERVER in a thread - t_server = threading.Thread(target=self.server) - #threads.append(t) - t_server.start() - #time.sleep(3) - - # RUN CLIENT to connect to server - #tele_client = SocketClient_UDP_TCP(HOST, PORT, "UDP") - with gemini.DeviceControllerTelescopeGemini(HOST, PORT) as tele_client: - # Only useful for TCP (does nothing for UDP) - tele_client._connect_to_device() - - # 1) SEND REQUEST data to server - - for data in q_a: - question,answer = data - tele_client.send_data(question) - - # 2) RECEIVE REPLY data from server - data_received = tele_client.receive_data() - self.assertEqual(data_received, answer) - - radec = tele_client.get_radec() - print("ra-dec is", radec) - #self.assertEqual(radec, ('15:01:48', '+12:28')) - self.assertEqual(radec.txt, '15:01:48,+12:28') - - #tele_client.close() - - #TODO: Stop the server thread (t_server), how can we do that ? - # cf https://www.oreilly.com/library/view/python-cookbook-2nd/0596007973/ch09s03.html - #t_server.suicide_toi() !!! - - -if __name__ == '__main__': - unittest.main() \ No newline at end of file -- libgit2 0.21.2