We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
QCoDeS monitor broken on master
The qcodes monitor is broken on the latest master. The error is occuring somewhere in the javascript as the data is still correctly sent over the websocket.

### Steps to reproduce
1. Create a monitor in QCoDeS.
2. Launch the monitor using `python -m qcodes.monitor.monitor`

### Expected behaviour
QCoDeS monitor displays in webbrowser.

### Actual behaviour
I get an error message in both Firefox and IE. (I don't have a copy of chrome to test on my experimental PC.

The error is: 
```
Status:error:I ran into the following problems at _.parameters[0].parameters[34].ts: Expecting null but instead got: "1581406601.743032" Expecting a Float but instead got: "1581406601.743032"
```

The issue seems to have crept in between revisions e5c5324 and 1ce7a23.

I've attached a sample response from the websocket in the working state and the broken state, where you can see that the timestamp is being sent as a string instead of a float...

[websocket_brokensample.txt](https://github.com/QCoDeS/Qcodes/files/4184596/websocket_brokensample.txt)
[websocket_workingsample.txt](https://github.com/QCoDeS/Qcodes/files/4184597/websocket_workingsample.txt)

### System
Windows 10
QCodes Master 33d4e71


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `qcodes/monitor/monitor.py`
Content:
```
1 #! /usr/bin/env python
2 # -*- coding: utf-8 -*-
3 # vim:fenc=utf-8
4 #
5 # Copyright © 2017 unga <giulioungaretti@me.com>
6 #
7 # Distributed under terms of the MIT license.
8 """
9 Monitor a set of parameters in a background thread
10 stream output over websocket
11 
12 To start monitor, run this file, or if qcodes is installed as a module:
13 
14 ``% python -m qcodes.monitor.monitor``
15 
16 Add parameters to monitor in your measurement by creating a new monitor with a
17 list of parameters to monitor:
18 
19 ``monitor = qcodes.Monitor(param1, param2, param3, ...)``
20 """
21 
22 
23 import sys
24 import logging
25 import os
26 import time
27 import json
28 from contextlib import suppress
29 from typing import Dict, Any, Optional, Sequence, Callable, Awaitable
30 from collections import defaultdict
31 
32 import asyncio
33 from asyncio import CancelledError
34 from threading import Thread, Event
35 
36 import socketserver
37 import webbrowser
38 import websockets
39 
40 from qcodes.instrument.parameter import Parameter
41 
42 if sys.version_info < (3, 7):
43     all_tasks = asyncio.Task.all_tasks
44 else:
45     all_tasks = asyncio.all_tasks
46 
47 WEBSOCKET_PORT = 5678
48 SERVER_PORT = 3000
49 
50 log = logging.getLogger(__name__)
51 
52 
53 def _get_metadata(*parameters: Parameter) -> Dict[str, Any]:
54     """
55     Return a dictionary that contains the parameter metadata grouped by the
56     instrument it belongs to.
57     """
58     metadata_timestamp = time.time()
59     # group metadata by instrument
60     metas: dict = defaultdict(list)
61     for parameter in parameters:
62         # Get the latest value from the parameter,
63         # respecting the max_val_age parameter
64         meta: Dict[str, Optional[str]] = {}
65         meta["value"] = str(parameter.get_latest())
66         timestamp = parameter.get_latest.get_timestamp()
67         if timestamp is not None:
68             meta["ts"] = str(timestamp.timestamp())
69         else:
70             meta["ts"] = None
71         meta["name"] = parameter.label or parameter.name
72         meta["unit"] = parameter.unit
73 
74         # find the base instrument that this parameter belongs to
75         baseinst = parameter.root_instrument
76         if baseinst is None:
77             metas["Unbound Parameter"].append(meta)
78         else:
79             metas[str(baseinst)].append(meta)
80 
81     # Create list of parameters, grouped by instrument
82     parameters_out = []
83     for instrument in metas:
84         temp = {"instrument": instrument, "parameters": metas[instrument]}
85         parameters_out.append(temp)
86 
87     state = {"ts": metadata_timestamp, "parameters": parameters_out}
88     return state
89 
90 
91 def _handler(parameters: Sequence[Parameter], interval: int) \
92         -> Callable[[websockets.WebSocketServerProtocol, str], Awaitable[None]]:
93     """
94     Return the websockets server handler.
95     """
96     async def server_func(websocket: websockets.WebSocketServerProtocol, _: str) -> None:
97         """
98         Create a websockets handler that sends parameter values to a listener
99         every "interval" seconds.
100         """
101         while True:
102             try:
103                 # Update the parameter values
104                 try:
105                     meta = _get_metadata(*parameters)
106                 except ValueError:
107                     log.exception("Error getting parameters")
108                     break
109                 log.debug("sending.. to %r", websocket)
110                 await websocket.send(json.dumps(meta))
111                 # Wait for interval seconds and then send again
112                 await asyncio.sleep(interval)
113             except (CancelledError, websockets.exceptions.ConnectionClosed):
114                 log.debug("Got CancelledError or ConnectionClosed",
115                           exc_info=True)
116                 break
117         log.debug("Closing websockets connection")
118 
119     return server_func
120 
121 
122 class Monitor(Thread):
123     """
124     QCodes Monitor - WebSockets server to monitor qcodes parameters.
125     """
126     running = None
127 
128     def __init__(self, *parameters: Parameter, interval: int = 1):
129         """
130         Monitor qcodes parameters.
131 
132         Args:
133             *parameters: Parameters to monitor.
134             interval: How often one wants to refresh the values.
135         """
136         super().__init__()
137 
138         # Check that all values are valid parameters
139         for parameter in parameters:
140             if not isinstance(parameter, Parameter):
141                 raise TypeError(f"We can only monitor QCodes "
142                                 f"Parameters, not {type(parameter)}")
143 
144         self.loop: Optional[asyncio.AbstractEventLoop] = None
145         self.server: Optional[websockets.WebSocketServer] = None
146         self._parameters = parameters
147         self.loop_is_closed = Event()
148         self.server_is_started = Event()
149         self.handler = _handler(parameters, interval=interval)
150 
151         log.debug("Start monitoring thread")
152         if Monitor.running:
153             # stop the old server
154             log.debug("Stopping and restarting server")
155             Monitor.running.stop()
156         self.start()
157 
158         # Wait until the loop is running
159         self.server_is_started.wait(timeout=5)
160         if not self.server_is_started.is_set():
161             raise RuntimeError("Failed to start server")
162         Monitor.running = self
163 
164     def run(self) -> None:
165         """
166         Start the event loop and run forever.
167         """
168         log.debug("Running Websocket server")
169         self.loop = asyncio.new_event_loop()
170         asyncio.set_event_loop(self.loop)
171         try:
172             server_start = websockets.serve(self.handler, '127.0.0.1',
173                                             WEBSOCKET_PORT, close_timeout=1)
174             self.server = self.loop.run_until_complete(server_start)
175             self.server_is_started.set()
176             self.loop.run_forever()
177         except OSError:
178             # The code above may throw an OSError
179             # if the socket cannot be bound
180             log.exception("Server could not be started")
181         finally:
182             log.debug("loop stopped")
183             log.debug("Pending tasks at close: %r",
184                       all_tasks(self.loop))
185             self.loop.close()
186             log.debug("loop closed")
187             self.loop_is_closed.set()
188 
189     def update_all(self) -> None:
190         """
191         Update all parameters in the monitor.
192         """
193         for parameter in self._parameters:
194             # call get if it can be called without arguments
195             with suppress(TypeError):
196                 parameter.get()
197 
198     def stop(self) -> None:
199         """
200         Shutdown the server, close the event loop and join the thread.
201         Setting active Monitor to ``None``.
202         """
203         self.join()
204         Monitor.running = None
205 
206     async def __stop_server(self) -> None:
207         log.debug("asking server %r to close", self.server)
208         if self.server is not None:
209             self.server.close()
210         log.debug("waiting for server to close")
211         if self.loop is not None and self.server is not None:
212             await self.loop.create_task(self.server.wait_closed())
213         log.debug("stopping loop")
214         if self.loop is not None:
215             log.debug("Pending tasks at stop: %r",
216                       all_tasks(self.loop))
217             self.loop.stop()
218 
219     def join(self, timeout: Optional[float] = None) -> None:
220         """
221         Overwrite ``Thread.join`` to make sure server is stopped before
222         joining avoiding a potential deadlock.
223         """
224         log.debug("Shutting down server")
225         if not self.is_alive():
226             # we run this check before trying to run to prevent a cryptic
227             # error message
228             log.debug("monitor is dead")
229             return
230         try:
231             if self.loop is not None:
232                 asyncio.run_coroutine_threadsafe(self.__stop_server(),
233                                                  self.loop)
234         except RuntimeError:
235             # the above may throw a runtime error if the loop is already
236             # stopped in which case there is nothing more to do
237             log.exception("Could not close loop")
238         self.loop_is_closed.wait(timeout=5)
239         if not self.loop_is_closed.is_set():
240             raise RuntimeError("Failed to join loop")
241         log.debug("Loop reported closed")
242         super().join(timeout=timeout)
243         log.debug("Monitor Thread has joined")
244 
245     @staticmethod
246     def show() -> None:
247         """
248         Overwrite this method to show/raise your monitor GUI
249         F.ex.
250 
251         ::
252 
253             import webbrowser
254             url = "localhost:3000"
255             # Open URL in new window, raising the window if possible.
256             webbrowser.open_new(url)
257 
258         """
259         webbrowser.open("http://localhost:{}".format(SERVER_PORT))
260 
261 
262 if __name__ == "__main__":
263     import http.server
264     # If this file is run, create a simple webserver that serves a simple
265     # website that can be used to view monitored parameters.
266     STATIC_DIR = os.path.join(os.path.dirname(__file__), 'dist')
267     os.chdir(STATIC_DIR)
268     try:
269         log.info("Starting HTTP Server at http://localhost:%i", SERVER_PORT)
270         with socketserver.TCPServer(("", SERVER_PORT),
271                                     http.server.SimpleHTTPRequestHandler) as httpd:
272             log.debug("serving directory %s", STATIC_DIR)
273             webbrowser.open("http://localhost:{}".format(SERVER_PORT))
274             httpd.serve_forever()
275     except KeyboardInterrupt:
276         log.info("Shutting Down HTTP Server")
277
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/qcodes/monitor/monitor.py b/qcodes/monitor/monitor.py
--- a/qcodes/monitor/monitor.py
+++ b/qcodes/monitor/monitor.py
@@ -26,7 +26,7 @@
 import time
 import json
 from contextlib import suppress
-from typing import Dict, Any, Optional, Sequence, Callable, Awaitable
+from typing import Dict, Union, Any, Optional, Sequence, Callable, Awaitable
 from collections import defaultdict
 
 import asyncio
@@ -61,11 +61,11 @@
     for parameter in parameters:
         # Get the latest value from the parameter,
         # respecting the max_val_age parameter
-        meta: Dict[str, Optional[str]] = {}
+        meta: Dict[str, Optional[Union[float, str]]] = {}
         meta["value"] = str(parameter.get_latest())
         timestamp = parameter.get_latest.get_timestamp()
         if timestamp is not None:
-            meta["ts"] = str(timestamp.timestamp())
+            meta["ts"] = timestamp.timestamp()
         else:
             meta["ts"] = None
         meta["name"] = parameter.label or parameter.name

{"golden_diff": "diff --git a/qcodes/monitor/monitor.py b/qcodes/monitor/monitor.py\n--- a/qcodes/monitor/monitor.py\n+++ b/qcodes/monitor/monitor.py\n@@ -26,7 +26,7 @@\n import time\n import json\n from contextlib import suppress\n-from typing import Dict, Any, Optional, Sequence, Callable, Awaitable\n+from typing import Dict, Union, Any, Optional, Sequence, Callable, Awaitable\n from collections import defaultdict\n \n import asyncio\n@@ -61,11 +61,11 @@\n     for parameter in parameters:\n         # Get the latest value from the parameter,\n         # respecting the max_val_age parameter\n-        meta: Dict[str, Optional[str]] = {}\n+        meta: Dict[str, Optional[Union[float, str]]] = {}\n         meta[\"value\"] = str(parameter.get_latest())\n         timestamp = parameter.get_latest.get_timestamp()\n         if timestamp is not None:\n-            meta[\"ts\"] = str(timestamp.timestamp())\n+            meta[\"ts\"] = timestamp.timestamp()\n         else:\n             meta[\"ts\"] = None\n         meta[\"name\"] = parameter.label or parameter.name\n", "issue": "QCoDeS monitor broken on master\nThe qcodes monitor is broken on the latest master. The error is occuring somewhere in the javascript as the data is still correctly sent over the websocket.\r\n\r\n### Steps to reproduce\r\n1. Create a monitor in QCoDeS.\r\n2. Launch the monitor using `python -m qcodes.monitor.monitor`\r\n\r\n### Expected behaviour\r\nQCoDeS monitor displays in webbrowser.\r\n\r\n### Actual behaviour\r\nI get an error message in both Firefox and IE. (I don't have a copy of chrome to test on my experimental PC.\r\n\r\nThe error is: \r\n```\r\nStatus:error:I ran into the following problems at _.parameters[0].parameters[34].ts: Expecting null but instead got: \"1581406601.743032\" Expecting a Float but instead got: \"1581406601.743032\"\r\n```\r\n\r\nThe issue seems to have crept in between revisions e5c5324 and 1ce7a23.\r\n\r\nI've attached a sample response from the websocket in the working state and the broken state, where you can see that the timestamp is being sent as a string instead of a float...\r\n\r\n[websocket_brokensample.txt](https://github.com/QCoDeS/Qcodes/files/4184596/websocket_brokensample.txt)\r\n[websocket_workingsample.txt](https://github.com/QCoDeS/Qcodes/files/4184597/websocket_workingsample.txt)\r\n\r\n### System\r\nWindows 10\r\nQCodes Master 33d4e71\r\n\n", "before_files": [{"content": "#! /usr/bin/env python\n# -*- coding: utf-8 -*-\n# vim:fenc=utf-8\n#\n# Copyright \u00a9 2017 unga <giulioungaretti@me.com>\n#\n# Distributed under terms of the MIT license.\n\"\"\"\nMonitor a set of parameters in a background thread\nstream output over websocket\n\nTo start monitor, run this file, or if qcodes is installed as a module:\n\n``% python -m qcodes.monitor.monitor``\n\nAdd parameters to monitor in your measurement by creating a new monitor with a\nlist of parameters to monitor:\n\n``monitor = qcodes.Monitor(param1, param2, param3, ...)``\n\"\"\"\n\n\nimport sys\nimport logging\nimport os\nimport time\nimport json\nfrom contextlib import suppress\nfrom typing import Dict, Any, Optional, Sequence, Callable, Awaitable\nfrom collections import defaultdict\n\nimport asyncio\nfrom asyncio import CancelledError\nfrom threading import Thread, Event\n\nimport socketserver\nimport webbrowser\nimport websockets\n\nfrom qcodes.instrument.parameter import Parameter\n\nif sys.version_info < (3, 7):\n    all_tasks = asyncio.Task.all_tasks\nelse:\n    all_tasks = asyncio.all_tasks\n\nWEBSOCKET_PORT = 5678\nSERVER_PORT = 3000\n\nlog = logging.getLogger(__name__)\n\n\ndef _get_metadata(*parameters: Parameter) -> Dict[str, Any]:\n    \"\"\"\n    Return a dictionary that contains the parameter metadata grouped by the\n    instrument it belongs to.\n    \"\"\"\n    metadata_timestamp = time.time()\n    # group metadata by instrument\n    metas: dict = defaultdict(list)\n    for parameter in parameters:\n        # Get the latest value from the parameter,\n        # respecting the max_val_age parameter\n        meta: Dict[str, Optional[str]] = {}\n        meta[\"value\"] = str(parameter.get_latest())\n        timestamp = parameter.get_latest.get_timestamp()\n        if timestamp is not None:\n            meta[\"ts\"] = str(timestamp.timestamp())\n        else:\n            meta[\"ts\"] = None\n        meta[\"name\"] = parameter.label or parameter.name\n        meta[\"unit\"] = parameter.unit\n\n        # find the base instrument that this parameter belongs to\n        baseinst = parameter.root_instrument\n        if baseinst is None:\n            metas[\"Unbound Parameter\"].append(meta)\n        else:\n            metas[str(baseinst)].append(meta)\n\n    # Create list of parameters, grouped by instrument\n    parameters_out = []\n    for instrument in metas:\n        temp = {\"instrument\": instrument, \"parameters\": metas[instrument]}\n        parameters_out.append(temp)\n\n    state = {\"ts\": metadata_timestamp, \"parameters\": parameters_out}\n    return state\n\n\ndef _handler(parameters: Sequence[Parameter], interval: int) \\\n        -> Callable[[websockets.WebSocketServerProtocol, str], Awaitable[None]]:\n    \"\"\"\n    Return the websockets server handler.\n    \"\"\"\n    async def server_func(websocket: websockets.WebSocketServerProtocol, _: str) -> None:\n        \"\"\"\n        Create a websockets handler that sends parameter values to a listener\n        every \"interval\" seconds.\n        \"\"\"\n        while True:\n            try:\n                # Update the parameter values\n                try:\n                    meta = _get_metadata(*parameters)\n                except ValueError:\n                    log.exception(\"Error getting parameters\")\n                    break\n                log.debug(\"sending.. to %r\", websocket)\n                await websocket.send(json.dumps(meta))\n                # Wait for interval seconds and then send again\n                await asyncio.sleep(interval)\n            except (CancelledError, websockets.exceptions.ConnectionClosed):\n                log.debug(\"Got CancelledError or ConnectionClosed\",\n                          exc_info=True)\n                break\n        log.debug(\"Closing websockets connection\")\n\n    return server_func\n\n\nclass Monitor(Thread):\n    \"\"\"\n    QCodes Monitor - WebSockets server to monitor qcodes parameters.\n    \"\"\"\n    running = None\n\n    def __init__(self, *parameters: Parameter, interval: int = 1):\n        \"\"\"\n        Monitor qcodes parameters.\n\n        Args:\n            *parameters: Parameters to monitor.\n            interval: How often one wants to refresh the values.\n        \"\"\"\n        super().__init__()\n\n        # Check that all values are valid parameters\n        for parameter in parameters:\n            if not isinstance(parameter, Parameter):\n                raise TypeError(f\"We can only monitor QCodes \"\n                                f\"Parameters, not {type(parameter)}\")\n\n        self.loop: Optional[asyncio.AbstractEventLoop] = None\n        self.server: Optional[websockets.WebSocketServer] = None\n        self._parameters = parameters\n        self.loop_is_closed = Event()\n        self.server_is_started = Event()\n        self.handler = _handler(parameters, interval=interval)\n\n        log.debug(\"Start monitoring thread\")\n        if Monitor.running:\n            # stop the old server\n            log.debug(\"Stopping and restarting server\")\n            Monitor.running.stop()\n        self.start()\n\n        # Wait until the loop is running\n        self.server_is_started.wait(timeout=5)\n        if not self.server_is_started.is_set():\n            raise RuntimeError(\"Failed to start server\")\n        Monitor.running = self\n\n    def run(self) -> None:\n        \"\"\"\n        Start the event loop and run forever.\n        \"\"\"\n        log.debug(\"Running Websocket server\")\n        self.loop = asyncio.new_event_loop()\n        asyncio.set_event_loop(self.loop)\n        try:\n            server_start = websockets.serve(self.handler, '127.0.0.1',\n                                            WEBSOCKET_PORT, close_timeout=1)\n            self.server = self.loop.run_until_complete(server_start)\n            self.server_is_started.set()\n            self.loop.run_forever()\n        except OSError:\n            # The code above may throw an OSError\n            # if the socket cannot be bound\n            log.exception(\"Server could not be started\")\n        finally:\n            log.debug(\"loop stopped\")\n            log.debug(\"Pending tasks at close: %r\",\n                      all_tasks(self.loop))\n            self.loop.close()\n            log.debug(\"loop closed\")\n            self.loop_is_closed.set()\n\n    def update_all(self) -> None:\n        \"\"\"\n        Update all parameters in the monitor.\n        \"\"\"\n        for parameter in self._parameters:\n            # call get if it can be called without arguments\n            with suppress(TypeError):\n                parameter.get()\n\n    def stop(self) -> None:\n        \"\"\"\n        Shutdown the server, close the event loop and join the thread.\n        Setting active Monitor to ``None``.\n        \"\"\"\n        self.join()\n        Monitor.running = None\n\n    async def __stop_server(self) -> None:\n        log.debug(\"asking server %r to close\", self.server)\n        if self.server is not None:\n            self.server.close()\n        log.debug(\"waiting for server to close\")\n        if self.loop is not None and self.server is not None:\n            await self.loop.create_task(self.server.wait_closed())\n        log.debug(\"stopping loop\")\n        if self.loop is not None:\n            log.debug(\"Pending tasks at stop: %r\",\n                      all_tasks(self.loop))\n            self.loop.stop()\n\n    def join(self, timeout: Optional[float] = None) -> None:\n        \"\"\"\n        Overwrite ``Thread.join`` to make sure server is stopped before\n        joining avoiding a potential deadlock.\n        \"\"\"\n        log.debug(\"Shutting down server\")\n        if not self.is_alive():\n            # we run this check before trying to run to prevent a cryptic\n            # error message\n            log.debug(\"monitor is dead\")\n            return\n        try:\n            if self.loop is not None:\n                asyncio.run_coroutine_threadsafe(self.__stop_server(),\n                                                 self.loop)\n        except RuntimeError:\n            # the above may throw a runtime error if the loop is already\n            # stopped in which case there is nothing more to do\n            log.exception(\"Could not close loop\")\n        self.loop_is_closed.wait(timeout=5)\n        if not self.loop_is_closed.is_set():\n            raise RuntimeError(\"Failed to join loop\")\n        log.debug(\"Loop reported closed\")\n        super().join(timeout=timeout)\n        log.debug(\"Monitor Thread has joined\")\n\n    @staticmethod\n    def show() -> None:\n        \"\"\"\n        Overwrite this method to show/raise your monitor GUI\n        F.ex.\n\n        ::\n\n            import webbrowser\n            url = \"localhost:3000\"\n            # Open URL in new window, raising the window if possible.\n            webbrowser.open_new(url)\n\n        \"\"\"\n        webbrowser.open(\"http://localhost:{}\".format(SERVER_PORT))\n\n\nif __name__ == \"__main__\":\n    import http.server\n    # If this file is run, create a simple webserver that serves a simple\n    # website that can be used to view monitored parameters.\n    STATIC_DIR = os.path.join(os.path.dirname(__file__), 'dist')\n    os.chdir(STATIC_DIR)\n    try:\n        log.info(\"Starting HTTP Server at http://localhost:%i\", SERVER_PORT)\n        with socketserver.TCPServer((\"\", SERVER_PORT),\n                                    http.server.SimpleHTTPRequestHandler) as httpd:\n            log.debug(\"serving directory %s\", STATIC_DIR)\n            webbrowser.open(\"http://localhost:{}\".format(SERVER_PORT))\n            httpd.serve_forever()\n    except KeyboardInterrupt:\n        log.info(\"Shutting Down HTTP Server\")\n", "path": "qcodes/monitor/monitor.py"}], "after_files": [{"content": "#! /usr/bin/env python\n# -*- coding: utf-8 -*-\n# vim:fenc=utf-8\n#\n# Copyright \u00a9 2017 unga <giulioungaretti@me.com>\n#\n# Distributed under terms of the MIT license.\n\"\"\"\nMonitor a set of parameters in a background thread\nstream output over websocket\n\nTo start monitor, run this file, or if qcodes is installed as a module:\n\n``% python -m qcodes.monitor.monitor``\n\nAdd parameters to monitor in your measurement by creating a new monitor with a\nlist of parameters to monitor:\n\n``monitor = qcodes.Monitor(param1, param2, param3, ...)``\n\"\"\"\n\n\nimport sys\nimport logging\nimport os\nimport time\nimport json\nfrom contextlib import suppress\nfrom typing import Dict, Union, Any, Optional, Sequence, Callable, Awaitable\nfrom collections import defaultdict\n\nimport asyncio\nfrom asyncio import CancelledError\nfrom threading import Thread, Event\n\nimport socketserver\nimport webbrowser\nimport websockets\n\nfrom qcodes.instrument.parameter import Parameter\n\nif sys.version_info < (3, 7):\n    all_tasks = asyncio.Task.all_tasks\nelse:\n    all_tasks = asyncio.all_tasks\n\nWEBSOCKET_PORT = 5678\nSERVER_PORT = 3000\n\nlog = logging.getLogger(__name__)\n\n\ndef _get_metadata(*parameters: Parameter) -> Dict[str, Any]:\n    \"\"\"\n    Return a dictionary that contains the parameter metadata grouped by the\n    instrument it belongs to.\n    \"\"\"\n    metadata_timestamp = time.time()\n    # group metadata by instrument\n    metas: dict = defaultdict(list)\n    for parameter in parameters:\n        # Get the latest value from the parameter,\n        # respecting the max_val_age parameter\n        meta: Dict[str, Optional[Union[float, str]]] = {}\n        meta[\"value\"] = str(parameter.get_latest())\n        timestamp = parameter.get_latest.get_timestamp()\n        if timestamp is not None:\n            meta[\"ts\"] = timestamp.timestamp()\n        else:\n            meta[\"ts\"] = None\n        meta[\"name\"] = parameter.label or parameter.name\n        meta[\"unit\"] = parameter.unit\n\n        # find the base instrument that this parameter belongs to\n        baseinst = parameter.root_instrument\n        if baseinst is None:\n            metas[\"Unbound Parameter\"].append(meta)\n        else:\n            metas[str(baseinst)].append(meta)\n\n    # Create list of parameters, grouped by instrument\n    parameters_out = []\n    for instrument in metas:\n        temp = {\"instrument\": instrument, \"parameters\": metas[instrument]}\n        parameters_out.append(temp)\n\n    state = {\"ts\": metadata_timestamp, \"parameters\": parameters_out}\n    return state\n\n\ndef _handler(parameters: Sequence[Parameter], interval: int) \\\n        -> Callable[[websockets.WebSocketServerProtocol, str], Awaitable[None]]:\n    \"\"\"\n    Return the websockets server handler.\n    \"\"\"\n    async def server_func(websocket: websockets.WebSocketServerProtocol, _: str) -> None:\n        \"\"\"\n        Create a websockets handler that sends parameter values to a listener\n        every \"interval\" seconds.\n        \"\"\"\n        while True:\n            try:\n                # Update the parameter values\n                try:\n                    meta = _get_metadata(*parameters)\n                except ValueError:\n                    log.exception(\"Error getting parameters\")\n                    break\n                log.debug(\"sending.. to %r\", websocket)\n                await websocket.send(json.dumps(meta))\n                # Wait for interval seconds and then send again\n                await asyncio.sleep(interval)\n            except (CancelledError, websockets.exceptions.ConnectionClosed):\n                log.debug(\"Got CancelledError or ConnectionClosed\",\n                          exc_info=True)\n                break\n        log.debug(\"Closing websockets connection\")\n\n    return server_func\n\n\nclass Monitor(Thread):\n    \"\"\"\n    QCodes Monitor - WebSockets server to monitor qcodes parameters.\n    \"\"\"\n    running = None\n\n    def __init__(self, *parameters: Parameter, interval: int = 1):\n        \"\"\"\n        Monitor qcodes parameters.\n\n        Args:\n            *parameters: Parameters to monitor.\n            interval: How often one wants to refresh the values.\n        \"\"\"\n        super().__init__()\n\n        # Check that all values are valid parameters\n        for parameter in parameters:\n            if not isinstance(parameter, Parameter):\n                raise TypeError(f\"We can only monitor QCodes \"\n                                f\"Parameters, not {type(parameter)}\")\n\n        self.loop: Optional[asyncio.AbstractEventLoop] = None\n        self.server: Optional[websockets.WebSocketServer] = None\n        self._parameters = parameters\n        self.loop_is_closed = Event()\n        self.server_is_started = Event()\n        self.handler = _handler(parameters, interval=interval)\n\n        log.debug(\"Start monitoring thread\")\n        if Monitor.running:\n            # stop the old server\n            log.debug(\"Stopping and restarting server\")\n            Monitor.running.stop()\n        self.start()\n\n        # Wait until the loop is running\n        self.server_is_started.wait(timeout=5)\n        if not self.server_is_started.is_set():\n            raise RuntimeError(\"Failed to start server\")\n        Monitor.running = self\n\n    def run(self) -> None:\n        \"\"\"\n        Start the event loop and run forever.\n        \"\"\"\n        log.debug(\"Running Websocket server\")\n        self.loop = asyncio.new_event_loop()\n        asyncio.set_event_loop(self.loop)\n        try:\n            server_start = websockets.serve(self.handler, '127.0.0.1',\n                                            WEBSOCKET_PORT, close_timeout=1)\n            self.server = self.loop.run_until_complete(server_start)\n            self.server_is_started.set()\n            self.loop.run_forever()\n        except OSError:\n            # The code above may throw an OSError\n            # if the socket cannot be bound\n            log.exception(\"Server could not be started\")\n        finally:\n            log.debug(\"loop stopped\")\n            log.debug(\"Pending tasks at close: %r\",\n                      all_tasks(self.loop))\n            self.loop.close()\n            log.debug(\"loop closed\")\n            self.loop_is_closed.set()\n\n    def update_all(self) -> None:\n        \"\"\"\n        Update all parameters in the monitor.\n        \"\"\"\n        for parameter in self._parameters:\n            # call get if it can be called without arguments\n            with suppress(TypeError):\n                parameter.get()\n\n    def stop(self) -> None:\n        \"\"\"\n        Shutdown the server, close the event loop and join the thread.\n        Setting active Monitor to ``None``.\n        \"\"\"\n        self.join()\n        Monitor.running = None\n\n    async def __stop_server(self) -> None:\n        log.debug(\"asking server %r to close\", self.server)\n        if self.server is not None:\n            self.server.close()\n        log.debug(\"waiting for server to close\")\n        if self.loop is not None and self.server is not None:\n            await self.loop.create_task(self.server.wait_closed())\n        log.debug(\"stopping loop\")\n        if self.loop is not None:\n            log.debug(\"Pending tasks at stop: %r\",\n                      all_tasks(self.loop))\n            self.loop.stop()\n\n    def join(self, timeout: Optional[float] = None) -> None:\n        \"\"\"\n        Overwrite ``Thread.join`` to make sure server is stopped before\n        joining avoiding a potential deadlock.\n        \"\"\"\n        log.debug(\"Shutting down server\")\n        if not self.is_alive():\n            # we run this check before trying to run to prevent a cryptic\n            # error message\n            log.debug(\"monitor is dead\")\n            return\n        try:\n            if self.loop is not None:\n                asyncio.run_coroutine_threadsafe(self.__stop_server(),\n                                                 self.loop)\n        except RuntimeError:\n            # the above may throw a runtime error if the loop is already\n            # stopped in which case there is nothing more to do\n            log.exception(\"Could not close loop\")\n        self.loop_is_closed.wait(timeout=5)\n        if not self.loop_is_closed.is_set():\n            raise RuntimeError(\"Failed to join loop\")\n        log.debug(\"Loop reported closed\")\n        super().join(timeout=timeout)\n        log.debug(\"Monitor Thread has joined\")\n\n    @staticmethod\n    def show() -> None:\n        \"\"\"\n        Overwrite this method to show/raise your monitor GUI\n        F.ex.\n\n        ::\n\n            import webbrowser\n            url = \"localhost:3000\"\n            # Open URL in new window, raising the window if possible.\n            webbrowser.open_new(url)\n\n        \"\"\"\n        webbrowser.open(\"http://localhost:{}\".format(SERVER_PORT))\n\n\nif __name__ == \"__main__\":\n    import http.server\n    # If this file is run, create a simple webserver that serves a simple\n    # website that can be used to view monitored parameters.\n    STATIC_DIR = os.path.join(os.path.dirname(__file__), 'dist')\n    os.chdir(STATIC_DIR)\n    try:\n        log.info(\"Starting HTTP Server at http://localhost:%i\", SERVER_PORT)\n        with socketserver.TCPServer((\"\", SERVER_PORT),\n                                    http.server.SimpleHTTPRequestHandler) as httpd:\n            log.debug(\"serving directory %s\", STATIC_DIR)\n            webbrowser.open(\"http://localhost:{}\".format(SERVER_PORT))\n            httpd.serve_forever()\n    except KeyboardInterrupt:\n        log.info(\"Shutting Down HTTP Server\")\n", "path": "qcodes/monitor/monitor.py"}]}