diff --git a/.coveragerc b/.coveragerc
index f1f0d805316265cd501992c55ebe5830fd2beeac..930614e9500e9f82a24b6c91b7a2589638470fce 100644
--- a/.coveragerc
+++ b/.coveragerc
@@ -2,7 +2,7 @@
 omit = */OldImporters/*
 
 [report]
-fail_under = 88.6
+fail_under = 89.8
 
 [html]
 directory = htmlcov
diff --git a/.editorconfig b/.editorconfig
index 3da771537e8e2ae420fb343e25249aa67b018e80..1049cc910a0fd71e257f76df11051c28cdfcdc3d 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -37,4 +37,4 @@ indent_style = space
 indent_size = 2
 
 [settings]
-known_third_party = docutils,jsonpickle,matplotlib,numpy,openqlab,pandas,pipenv,pkg_resources,recommonmark,scipy,serial,setuptools,sphinx,sphinx_rtd_theme,tabulate
+known_third_party = docutils,jsonpickle,matplotlib,mock_visa,numpy,openqlab,pandas,pipenv,pkg_resources,pyvisa,recommonmark,scipy,serial,setuptools,sphinx,sphinx_rtd_theme,tabulate
diff --git a/Pipfile b/Pipfile
index 0f7a0d51e3d4020eef79b44ec5965c22d3f06d9a..2dc5ff3c723484981b99399a4f795cd129f0449e 100644
--- a/Pipfile
+++ b/Pipfile
@@ -43,6 +43,8 @@ six = ">=1.12.0"
 python-dateutil = ">=2.7.5"
 eml-parser = ">=1.11"
 DateTime = "*"
+pyvisa = "*"
+pyvisa-py = "*"
 docutils = "==0.15.2"
 
 [pipenv]
diff --git a/Pipfile.lock b/Pipfile.lock
index 3a12011d991b505f1e9de9df56ed21ac8926d4a8..a42fe0a884e52e4b4ea9122c836fcd687c59d69c 100644
--- a/Pipfile.lock
+++ b/Pipfile.lock
@@ -1,7 +1,7 @@
 {
     "_meta": {
         "hash": {
-            "sha256": "f299b4e9164a13ece2c350e6f4f89b382af1ee8ad67652e7d0689d61337fbc25"
+            "sha256": "9d7ae5c788236430d06b2e57f04d083e030aa9303a5aa9b23f94e12d9169dbfe"
         },
         "pipfile-spec": 6,
         "requires": {},
@@ -187,30 +187,30 @@
         },
         "numpy": {
             "hashes": [
-                "sha256:2bc9c62dfc893626cdd50dce30f597f387c4aca11cd2cdffe3e7e06a4fe19ee6",
-                "sha256:3701ddb007a0549c12b26e90c82d520b9c4acf801705b9334654ade2a9550b75",
-                "sha256:4c51a496ec1ce170f2b5eb458c0441affc5b1fd1d7272cf322b443c90c69983a",
-                "sha256:6beebdc222e214bbbafab8f089a65821cff6cad25b349eabb653490dc25342fd",
-                "sha256:70319f4d4e0a0c94e04922aaaf8c9aca72d38b61cc69e338fb872e9ff27d94a2",
-                "sha256:73abd855401b9da6efb67490f4dd82226cf95f47d66b8dc9ebe4df523baaaeb2",
-                "sha256:76d92b0b86227c6dbc3f9030b8b865f41ae04f73daa161e4e3c5566e03d6ed41",
-                "sha256:787781333c1d69c7c23ccd85165cca732a5f3fd9d997b8ee40829b7c0c38db86",
-                "sha256:78ca6befab03c682bcb013241801e3b750e9de9b60664e9839e5b2098b5580ee",
-                "sha256:7b0b915190cf60e691c17147f5d955e273d4c482b795a7bb168ad4a2fe2fb180",
-                "sha256:9b369822f7681bc36b6ba624bc8ea0a1a456a9f72b324070d89ee2856cba62be",
-                "sha256:a8081de993fd47b9cb7376935bb1781118fd2c473a0e834601e28229275f78d6",
-                "sha256:c207646355d1d04c054d781ddcd100bbd5afc69f75f8a7623317b9db41a2015f",
-                "sha256:c9474a8fe03ca958e6fafefee13d6b4a45ea4ed7e35261abf61899c0f599a118",
-                "sha256:c9e73aa60166609c80f4285af8ba8b9b79e5a935df6927a174637c7c08fa8e67",
-                "sha256:cbd2e1c1fb61b17eca745d7f9f6d684fd7f7817bef6454890eb6fa3e1cd4905a",
-                "sha256:d7b16541a6e970d402587ff2f1cbd85753c8d982a0d5894991505fea9b81a52b",
-                "sha256:e89027b24027dade03929bc17adee23feec1f6f707f4ea366c5d5a342d4d81a1",
-                "sha256:e9907b5ea505dcd2cfaeb6ab08f241047ba651611f3974c9d624a7c5066ab3a6",
-                "sha256:f676739da486d1c7de2d9450dafca6dee04f4d3e881b8761b795865ef1872eaf",
-                "sha256:fa8851d10af0739adcae54acf5706953e2e45752f4a550006f6f3aff92335566"
+                "sha256:03bbde29ac8fba860bb2c53a1525b3604a9b60417855ac3119d89868ec6041c3",
+                "sha256:1baefd1fb4695e7f2e305467dbd876d765e6edd30c522894df76f8301efaee36",
+                "sha256:1c35fb1131362e6090d30286cfda52ddd42e69d3e2bf1fea190a0fad83ea3a18",
+                "sha256:3c68c827689ca0ca713dba598335073ce0966850ec0b30715527dce4ecd84055",
+                "sha256:443ab93fc35b31f01db8704681eb2fd82f3a1b2fa08eed2dd0e71f1f57423d4a",
+                "sha256:56710a756c5009af9f35b91a22790701420406d9ac24cf6b652b0e22cfbbb7ff",
+                "sha256:62506e9e4d2a39c87984f081a2651d4282a1d706b1a82fe9d50a559bb58e705a",
+                "sha256:6f8113c8dbfc192b58996ee77333696469ea121d1c44ea429d8fd266e4c6be51",
+                "sha256:712f0c32555132f4b641b918bdb1fd3c692909ae916a233ce7f50eac2de87e37",
+                "sha256:854f6ed4fa91fa6da5d764558804ba5b0f43a51e5fe9fc4fdc93270b052f188a",
+                "sha256:88c5ccbc4cadf39f32193a5ef22e3f84674418a9fd877c63322917ae8f295a56",
+                "sha256:905cd6fa6ac14654a6a32b21fad34670e97881d832e24a3ca32e19b455edb4a8",
+                "sha256:9d6de2ad782aae68f7ed0e0e616477fbf693d6d7cc5f0f1505833ff12f84a673",
+                "sha256:a30f5c3e1b1b5d16ec1f03f4df28e08b8a7529d8c920bbed657f4fde61f1fbcd",
+                "sha256:a9d72d9abaf65628f0f31bbb573b7d9304e43b1e6bbae43149c17737a42764c4",
+                "sha256:ac3cf835c334fcc6b74dc4e630f9b5ff7b4c43f7fb2a7813208d95d4e10b5623",
+                "sha256:b091e5d4cbbe79f0e8b6b6b522346e54a282eadb06e3fd761e9b6fafc2ca91ad",
+                "sha256:cc070fc43a494e42732d6ae2f6621db040611c1dde64762a40c8418023af56d7",
+                "sha256:e1080e37c090534adb2dd7ae1c59ee883e5d8c3e63d2a4d43c20ee348d0459c5",
+                "sha256:f084d513de729ff10cd72a1f80db468cff464fedb1ef2fea030221a0f62d7ff4",
+                "sha256:f6a7421da632fc01e8a3ecd19c3f7350258d82501a646747664bae9c6a87c731"
             ],
             "index": "pypi",
-            "version": "==1.18.0rc1"
+            "version": "==1.18.0"
         },
         "pandas": {
             "hashes": [
@@ -270,10 +270,10 @@
         },
         "pyparsing": {
             "hashes": [
-                "sha256:20f995ecd72f2a1f4bf6b072b63b22e2eb457836601e76d6e5dfcd75436acc1f",
-                "sha256:4ca62001be367f01bd3e92ecbb79070272a9d4964dce6a48a82ff0b8bc7e683a"
+                "sha256:4c830582a84fb022400b85429791bc551f1f4871c33f23e44f353119e92f969f",
+                "sha256:c342dccb5250c08d45fd6f8b4a559613ca603b57498511740e65cd11a2e7dcec"
             ],
-            "version": "==2.4.5"
+            "version": "==2.4.6"
         },
         "pyserial": {
             "hashes": [
@@ -307,6 +307,21 @@
             "index": "pypi",
             "version": "==2019.3"
         },
+        "pyvisa": {
+            "hashes": [
+                "sha256:ba936e2943ae29d7e33c3df00077d554c6137b02d097457ed4f36d77977e3293"
+            ],
+            "index": "pypi",
+            "version": "==1.10.1"
+        },
+        "pyvisa-py": {
+            "hashes": [
+                "sha256:1c918650a770a634434f6e8e0e1c097b5f0f80dc23bc60d3779c9890b4aa9150",
+                "sha256:3f907cadd10cd40955ed5aa90e42a5bf0c0d2abc6077895c940dbaf75b7666b7"
+            ],
+            "index": "pypi",
+            "version": "==0.3.1"
+        },
         "requests": {
             "hashes": [
                 "sha256:11e007a8a2aa0323f5a921e9e6a2d7e4e67d9877e85773fba9ba6419025cbeb4",
@@ -317,30 +332,30 @@
         },
         "scipy": {
             "hashes": [
-                "sha256:1b51721e5792c4d722e0cab4daf82187d5f22f2e42c56cd247398a16e4d2f48d",
-                "sha256:1e46825a4b810e2c36cfebecbad09edfd14b74a43f6146343ec96c62659e4573",
-                "sha256:203ba7ab52270e392e7e43834ffbe94fae09ca7f03113a11170cd36d0826eece",
-                "sha256:204667ab336e9087a9bcd2be37b540703aef7b6f9a4fd776186d18b8b972f24a",
-                "sha256:2d5b67afa5f5e0cf60dc747327c5be15cde434bed9c96743019886f86bfe0918",
-                "sha256:31f7cfa93b01507c935c12b535e24812594002a02a56803d7cd063e9920d25e8",
-                "sha256:39e9571ae080c149f4b5324836e7ee06a7e331e77f0d6d55f9c6ab7266cd875a",
-                "sha256:4061d71c0f143f3985d2ea9ebc4c1d86eff6df79318f265a5fdb23527811f5b2",
-                "sha256:461ca398ac6cb46bc89bdbc9f6a81c0302ee370cebe8be682c84a51977e21ded",
-                "sha256:6fcc1e8b47c332bb0a558d7529ede0990ea5ff822a9a4392b003adad8e0b31b8",
-                "sha256:72e644285a39aa64aaef7373fcbbed998aa7a550dc073d910d712a9380eba8b1",
-                "sha256:ab35718a2dd58089fbbdf3bcf1bb368a2ec99314de58f65d5d9a1cc8a1f49419",
-                "sha256:b51597ed8c1d3f2f07e537fb492a2c3659e477c502ad93c7e241f39ae9e21a78",
-                "sha256:bbb21e998e136e1fdfd77e808abf16c72e75c6ffbb87f4b5a41f19c9a4b3e2de",
-                "sha256:c1fe5826d26b684d5a690099d56f010a4bdd916e32f9fc0e0898c22551fdc635",
-                "sha256:c2722813ff63bb1f0fb3c75253a85a16c2b0ecc8c4f999f666a7788bf7c6e4c3",
-                "sha256:cdbcfbe97a21fc6b3c55991a7bcd6a278a38bd197e2d274a45611cdc91beb5c0",
-                "sha256:e47f4d0be4fc6985c789d24898ad65daec781c7f5da4a3d4c76ed9af285306ba",
-                "sha256:e588454fd6899d93b73d648d4d90d3d1285c91d08530a6ab94826676c862cabc",
-                "sha256:fa39935d7db5066a1b88575fdb196bb576474a17987637430df6bede311906be",
-                "sha256:fb4b6c26e39375e0cb56d05508c1b43515589725f903c9250940fca906e909fd"
+                "sha256:00af72998a46c25bdb5824d2b729e7dabec0c765f9deb0b504f928591f5ff9d4",
+                "sha256:0902a620a381f101e184a958459b36d3ee50f5effd186db76e131cbefcbb96f7",
+                "sha256:1e3190466d669d658233e8a583b854f6386dd62d655539b77b3fa25bfb2abb70",
+                "sha256:2cce3f9847a1a51019e8c5b47620da93950e58ebc611f13e0d11f4980ca5fecb",
+                "sha256:3092857f36b690a321a662fe5496cb816a7f4eecd875e1d36793d92d3f884073",
+                "sha256:71eb180f22c49066f25d6df16f8709f215723317cc951d99e54dc88020ea57be",
+                "sha256:770254a280d741dd3436919d47e35712fb081a6ff8bafc0f319382b954b77802",
+                "sha256:787cc50cab3020a865640aba3485e9fbd161d4d3b0d03a967df1a2881320512d",
+                "sha256:8a07760d5c7f3a92e440ad3aedcc98891e915ce857664282ae3c0220f3301eb6",
+                "sha256:8d3bc3993b8e4be7eade6dcc6fd59a412d96d3a33fa42b0fa45dc9e24495ede9",
+                "sha256:9508a7c628a165c2c835f2497837bf6ac80eb25291055f56c129df3c943cbaf8",
+                "sha256:a144811318853a23d32a07bc7fd5561ff0cac5da643d96ed94a4ffe967d89672",
+                "sha256:a1aae70d52d0b074d8121333bc807a485f9f1e6a69742010b33780df2e60cfe0",
+                "sha256:a2d6df9eb074af7f08866598e4ef068a2b310d98f87dc23bd1b90ec7bdcec802",
+                "sha256:bb517872058a1f087c4528e7429b4a44533a902644987e7b2fe35ecc223bc408",
+                "sha256:c5cac0c0387272ee0e789e94a570ac51deb01c796b37fb2aad1fb13f85e2f97d",
+                "sha256:cc971a82ea1170e677443108703a2ec9ff0f70752258d0e9f5433d00dda01f59",
+                "sha256:dba8306f6da99e37ea08c08fef6e274b5bf8567bb094d1dbe86a20e532aca088",
+                "sha256:dc60bb302f48acf6da8ca4444cfa17d52c63c5415302a9ee77b3b21618090521",
+                "sha256:dce415575d28db840608afbf92d290e9689b741c6dda8d91871438966519203f",
+                "sha256:dee1bbf3a6c8f73b6b218cb28eed8dd13347ea2f87d572ce19b289d6fd3fbc59"
             ],
             "index": "pypi",
-            "version": "==1.4.0"
+            "version": "==1.4.1"
         },
         "six": {
             "hashes": [
@@ -352,31 +367,27 @@
         },
         "statsmodels": {
             "hashes": [
-                "sha256:172d947a1a8542eefe7155f73ead951f6748782846e8e97e73b8ebf6483d5820",
-                "sha256:1d61edd4b73f1f4ef983007cd0d797e6cafd9e7de0125584761d93f79675cdd2",
-                "sha256:3b22ed234b80cd9d38988fba487da89cae484a4c2b9088b3e7783c0a937a4568",
-                "sha256:4d1c4b4ecdff539c7fea788f919ed25f25fdc50189a5c0b1a1a966bf04f3a2b8",
-                "sha256:65f08024ef9a0bd40b63c455d02da34394658c53c16e3494dd6171e86e639f9b",
-                "sha256:6cb8395c820eda886721ec2e0db41ffebb2c5a090b27ef9bf51f64e9ab1486af",
-                "sha256:75d8e36693d3e7459df9d4e5e4761b0277dd1c9d028a60ef6876e8299f8b9cd7",
-                "sha256:7db46146a5e773c9905d914735fabc1e7b3b1027adc98e42060ad8e61460e647",
-                "sha256:81c1a0a9dcc46713f47a5b69b1e7d74041ed85f774009302deedad11786beee0",
-                "sha256:8954e9f1659e99a6684167432d36e25bf4e739bb3ae99709a021490f11992fed",
-                "sha256:938dcf95b37fdd6404e0c1f2035b51de0cfd63a63faa9048751176ea55cb4ce9",
-                "sha256:9c9de647fa3ed058635e6a1b1bb2d3d2396f103cb1dcd00d5e70c36d8137639d",
-                "sha256:9cd2194c6642a8754e85f9a6e6912cdf996bebf6ff715d3cc67f65dadfd37cc9",
-                "sha256:a7e6d67d986ef9776b80d30fba4dca1db534986ee2023bab121d70bd78d06c49",
-                "sha256:a8848b368838a62d06c9f0995bdc6f5e82e09f55440bd85c2c13c58777813ebc",
-                "sha256:be0436f7ea14c277f30e3175dd463d2a26226d8fd360d06bb1c7cd1d9bbedafc",
-                "sha256:c84625b8746ee7e613752334bfb9ff60dffb91d22f19b7f08b8c387776dc42c1",
-                "sha256:e3d36ff3653b50554d9ad2c95556224d1882a5db328eea8a9aa32a0eb08e4e80",
-                "sha256:e67e244ae4ca988b40783db7e1bac0de5b7f9f91062cdc0c93e219b189700388",
-                "sha256:eb0589fae351e123d8dbf0a8fbfd9e9cc033622b0e9f6e81684aa692be48688d",
-                "sha256:ede6c56ae5d67bf068c4486f970f24b10b952d85dab0b44e7761749159a90d7f",
-                "sha256:f797eada017668ca4c8c50664ff44437c461dc20233da0e12ff81327a13877fd"
+                "sha256:050485ba95f0a09e176b14a1032b14d4f906cc437933634dcdd9a25ec49ad777",
+                "sha256:26a53525ddd8c53f16434306d9b80397ac433e5208f8a60e5a165304d795969f",
+                "sha256:37cc519c5174a8c3dbcfd03977bf60a7906eca72a4be7918efd95709b9a9b729",
+                "sha256:451e452da0c284906913866c809f436f876c14d073c5dda2bf9db122baed3358",
+                "sha256:495be0ef9fed694b1aba0cea544facdb79f389ad17eb68f031deb3c87547a0f6",
+                "sha256:53639829cb1453202298a1b98ddb145249f07dec08a0ebfb2dadd370f8913574",
+                "sha256:5d964437b8b1a75b2867b634f342b0af17e37d566a4af280b34f592cdb99cac9",
+                "sha256:67c4fe022395c0f9211fa2247c3ba8c3c84a5b3693ea4841bf0abd3c76d24330",
+                "sha256:74437f95301ebad6fe9355152103d051ad4dcdf4bbf9dc868a2f825338cf1314",
+                "sha256:74b13718fff0d54cb11984f4a3ea5e4f928f48551e6b6b8da8a109825d36799e",
+                "sha256:8154f837094f0867fe5fd0558dd75e9b4dd6e88eeeba7fd10f6b4bc1a94f4f62",
+                "sha256:892cd89de060a42032a9eafe32b40a128dc0386eaa6cdc9bc177f34425dcd563",
+                "sha256:903231076d77f9df6c8cb9e2c8abc31f97a764f477cd0909bc7f57d70a3c280c",
+                "sha256:958740ec79717b7c26f7f654744335ce373ab2ce7662b80d1231101d835db141",
+                "sha256:b3c4f479219b006c5c951b14b1bfd72473511c9fdcde01606e733d6f33bb902f",
+                "sha256:c407cc2a7faee7ec1c6b9a2e41541ff0d5081641dcdb29dccccf1f55131f2421",
+                "sha256:c791e5a0ef3e321b087cc6971878579375269d29f91475bbbbea391ce47ebf17",
+                "sha256:f834f7a529e521bd522522f763137f810913a6540d7a7947a95bba5515da2974"
             ],
             "index": "pypi",
-            "version": "==0.10.2"
+            "version": "==0.11.0rc1"
         },
         "tabulate": {
             "hashes": [
@@ -452,14 +463,6 @@
             ],
             "version": "==1.4.3"
         },
-        "appnope": {
-            "hashes": [
-                "sha256:5b26757dc6f79a3b7dc9fab95359328d5747fcb2409d331ea66d0272b90ab2a0",
-                "sha256:8b995ffe925347a2138d7ac0fe77155e4311a0ea6d6da4f5128fe4b3cbe5ed71"
-            ],
-            "markers": "sys_platform == 'darwin'",
-            "version": "==0.1.0"
-        },
         "aspy.refactor-imports": {
             "hashes": [
                 "sha256:6d465c18cadad7e5a87810ecf8e516cb6f78e91871f4f55d0f228c6c376bd16a",
@@ -490,10 +493,10 @@
         },
         "babel": {
             "hashes": [
-                "sha256:af92e6106cb7c55286b25b38ad7695f8b4efb36a90ba483d7f7a6628c46158ab",
-                "sha256:e86135ae101e31e2c8ec20a4e0c5220f4eed12487d5cf3f78be7e98d3a57fc28"
+                "sha256:1aac2ae2d0d8ea368fa90906567f5c08463d98ade155c0c4bfedd6a0f7160e38",
+                "sha256:d670ea0b10f8b723672d3a6abeb87b565b244da220d76b4dba1b66269ec152d4"
             ],
-            "version": "==2.7.0"
+            "version": "==2.8.0"
         },
         "backcall": {
             "hashes": [
@@ -531,6 +534,44 @@
             ],
             "version": "==2019.11.28"
         },
+        "cffi": {
+            "hashes": [
+                "sha256:0b49274afc941c626b605fb59b59c3485c17dc776dc3cc7cc14aca74cc19cc42",
+                "sha256:0e3ea92942cb1168e38c05c1d56b0527ce31f1a370f6117f1d490b8dcd6b3a04",
+                "sha256:135f69aecbf4517d5b3d6429207b2dff49c876be724ac0c8bf8e1ea99df3d7e5",
+                "sha256:19db0cdd6e516f13329cba4903368bff9bb5a9331d3410b1b448daaadc495e54",
+                "sha256:2781e9ad0e9d47173c0093321bb5435a9dfae0ed6a762aabafa13108f5f7b2ba",
+                "sha256:291f7c42e21d72144bb1c1b2e825ec60f46d0a7468f5346841860454c7aa8f57",
+                "sha256:2c5e309ec482556397cb21ede0350c5e82f0eb2621de04b2633588d118da4396",
+                "sha256:2e9c80a8c3344a92cb04661115898a9129c074f7ab82011ef4b612f645939f12",
+                "sha256:32a262e2b90ffcfdd97c7a5e24a6012a43c61f1f5a57789ad80af1d26c6acd97",
+                "sha256:3c9fff570f13480b201e9ab69453108f6d98244a7f495e91b6c654a47486ba43",
+                "sha256:415bdc7ca8c1c634a6d7163d43fb0ea885a07e9618a64bda407e04b04333b7db",
+                "sha256:42194f54c11abc8583417a7cf4eaff544ce0de8187abaf5d29029c91b1725ad3",
+                "sha256:4424e42199e86b21fc4db83bd76909a6fc2a2aefb352cb5414833c030f6ed71b",
+                "sha256:4a43c91840bda5f55249413037b7a9b79c90b1184ed504883b72c4df70778579",
+                "sha256:599a1e8ff057ac530c9ad1778293c665cb81a791421f46922d80a86473c13346",
+                "sha256:5c4fae4e9cdd18c82ba3a134be256e98dc0596af1e7285a3d2602c97dcfa5159",
+                "sha256:5ecfa867dea6fabe2a58f03ac9186ea64da1386af2159196da51c4904e11d652",
+                "sha256:62f2578358d3a92e4ab2d830cd1c2049c9c0d0e6d3c58322993cc341bdeac22e",
+                "sha256:6471a82d5abea994e38d2c2abc77164b4f7fbaaf80261cb98394d5793f11b12a",
+                "sha256:6d4f18483d040e18546108eb13b1dfa1000a089bcf8529e30346116ea6240506",
+                "sha256:71a608532ab3bd26223c8d841dde43f3516aa5d2bf37b50ac410bb5e99053e8f",
+                "sha256:74a1d8c85fb6ff0b30fbfa8ad0ac23cd601a138f7509dc617ebc65ef305bb98d",
+                "sha256:7b93a885bb13073afb0aa73ad82059a4c41f4b7d8eb8368980448b52d4c7dc2c",
+                "sha256:7d4751da932caaec419d514eaa4215eaf14b612cff66398dd51129ac22680b20",
+                "sha256:7f627141a26b551bdebbc4855c1157feeef18241b4b8366ed22a5c7d672ef858",
+                "sha256:8169cf44dd8f9071b2b9248c35fc35e8677451c52f795daa2bb4643f32a540bc",
+                "sha256:aa00d66c0fab27373ae44ae26a66a9e43ff2a678bf63a9c7c1a9a4d61172827a",
+                "sha256:ccb032fda0873254380aa2bfad2582aedc2959186cce61e3a17abc1a55ff89c3",
+                "sha256:d754f39e0d1603b5b24a7f8484b22d2904fa551fe865fd0d4c3332f078d20d4e",
+                "sha256:d75c461e20e29afc0aee7172a0950157c704ff0dd51613506bd7d82b718e7410",
+                "sha256:dcd65317dd15bc0451f3e01c80da2216a31916bdcffd6221ca1202d96584aa25",
+                "sha256:e570d3ab32e2c2861c4ebe6ffcad6a8abf9347432a37608fe1fbd157b3f0036b",
+                "sha256:fd43a88e045cf992ed09fa724b5315b790525f2676883a6ea64e3263bae6549d"
+            ],
+            "version": "==1.13.2"
+        },
         "cfgv": {
             "hashes": [
                 "sha256:edb387943b665bf9c434f717bf630fa78aecd53d5900d2e05da6ad6048553144",
@@ -561,39 +602,65 @@
         },
         "coverage": {
             "hashes": [
-                "sha256:0cd13a6e98c37b510a2d34c8281d5e1a226aaf9b65b7d770ef03c63169965351",
-                "sha256:1a4b6b6a2a3a6612e6361130c2cc3dc4378d8c221752b96167ccbad94b47f3cd",
-                "sha256:2ee55e6dba516ddf6f484aa83ccabbb0adf45a18892204c23486938d12258cde",
-                "sha256:3be5338a2eb4ef03c57f20917e1d12a1fd10e3853fed060b6d6b677cb3745898",
-                "sha256:44b783b02db03c4777d8cf71bae19eadc171a6f2a96777d916b2c30a1eb3d070",
-                "sha256:475bf7c4252af0a56e1abba9606f1e54127cdf122063095c75ab04f6f99cf45e",
-                "sha256:47c81ee687eafc2f1db7f03fbe99aab81330565ebc62fb3b61edfc2216a550c8",
-                "sha256:4a7f8e72b18f2aca288ff02255ce32cc830bc04d993efbc87abf6beddc9e56c0",
-                "sha256:50197163a22fd17f79086e087a787883b3ec9280a509807daf158dfc2a7ded02",
-                "sha256:56b13000acf891f700f5067512b804d1ec8c301d627486c678b903859d07f798",
-                "sha256:79388ae29c896299b3567965dbcd93255f175c17c6c7bca38614d12718c47466",
-                "sha256:79fd5d3d62238c4f583b75d48d53cdae759fe04d4fb18fe8b371d88ad2b6f8be",
-                "sha256:7fe3e2fde2bf1d7ce25ebcd2d3de3650b8d60d9a73ce6dcef36e20191291613d",
-                "sha256:81042a24f67b96e4287774014fa27220d8a4d91af1043389e4d73892efc89ac6",
-                "sha256:81326f1095c53111f8afc95da281e1414185f4a538609a77ca50bdfa39a6c207",
-                "sha256:8873dc0d8f42142ea9f20c27bbdc485190fff93823c6795be661703369e5877d",
-                "sha256:88d2cbcb0a112f47eef71eb95460b6995da18e6f8ca50c264585abc2c473154b",
-                "sha256:91f2491aeab9599956c45a77c5666d323efdec790bfe23fcceafcd91105d585a",
-                "sha256:979daa8655ae5a51e8e7a24e7d34e250ae8309fd9719490df92cbb2fe2b0422b",
-                "sha256:9c871b006c878a890c6e44a5b2f3c6291335324b298c904dc0402ee92ee1f0be",
-                "sha256:a6d092545e5af53e960465f652e00efbf5357adad177b2630d63978d85e46a72",
-                "sha256:b5ed7837b923d1d71c4f587ae1539ccd96bfd6be9788f507dbe94dab5febbb5d",
-                "sha256:ba259f68250f16d2444cbbfaddaa0bb20e1560a4fdaad50bece25c199e6af864",
-                "sha256:be1d89614c6b6c36d7578496dc8625123bda2ff44f224cf8b1c45b810ee7383f",
-                "sha256:c1b030a79749aa8d1f1486885040114ee56933b15ccfc90049ba266e4aa2139f",
-                "sha256:c95bb147fab76f2ecde332d972d8f4138b8f2daee6c466af4ff3b4f29bd4c19e",
-                "sha256:d52c1c2d7e856cecc05aa0526453cb14574f821b7f413cc279b9514750d795c1",
-                "sha256:d609a6d564ad3d327e9509846c2c47f170456344521462b469e5cb39e48ba31c",
-                "sha256:e1bad043c12fb58e8c7d92b3d7f2f49977dcb80a08a6d1e7a5114a11bf819fca",
-                "sha256:e5a675f6829c53c87d79117a8eb656cc4a5f8918185a32fc93ba09778e90f6db",
-                "sha256:fec32646b98baf4a22fdceb08703965bd16dea09051fbeb31a04b5b6e72b846c"
-            ],
-            "version": "==5.0"
+                "sha256:0101888bd1592a20ccadae081ba10e8b204d20235d18d05c6f7d5e904a38fc10",
+                "sha256:04b961862334687549eb91cd5178a6fbe977ad365bddc7c60f2227f2f9880cf4",
+                "sha256:1ca43dbd739c0fc30b0a3637a003a0d2c7edc1dd618359d58cc1e211742f8bd1",
+                "sha256:1cbb88b34187bdb841f2599770b7e6ff8e259dc3bb64fc7893acf44998acf5f8",
+                "sha256:232f0b52a5b978288f0bbc282a6c03fe48cd19a04202df44309919c142b3bb9c",
+                "sha256:24bcfa86fd9ce86b73a8368383c39d919c497a06eebb888b6f0c12f13e920b1a",
+                "sha256:25b8f60b5c7da71e64c18888f3067d5b6f1334b9681876b2fb41eea26de881ae",
+                "sha256:2714160a63da18aed9340c70ed514973971ee7e665e6b336917ff4cca81a25b1",
+                "sha256:2ca2cd5264e84b2cafc73f0045437f70c6378c0d7dbcddc9ee3fe192c1e29e5d",
+                "sha256:2cc707fc9aad2592fc686d63ef72dc0031fc98b6fb921d2f5395d9ab84fbc3ef",
+                "sha256:348630edea485f4228233c2f310a598abf8afa5f8c716c02a9698089687b6085",
+                "sha256:40fbfd6b044c9db13aeec1daf5887d322c710d811f944011757526ef6e323fd9",
+                "sha256:46c9c6a1d1190c0b75ec7c0f339088309952b82ae8d67a79ff1319eb4e749b96",
+                "sha256:591506e088901bdc25620c37aec885e82cc896528f28c57e113751e3471fc314",
+                "sha256:5ac71bba1e07eab403b082c4428f868c1c9e26a21041436b4905c4c3d4e49b08",
+                "sha256:5f622f19abda4e934938e24f1d67599249abc201844933a6f01aaa8663094489",
+                "sha256:65bead1ac8c8930cf92a1ccaedcce19a57298547d5d1db5c9d4d068a0675c38b",
+                "sha256:7362a7f829feda10c7265b553455de596b83d1623b3d436b6d3c51c688c57bf6",
+                "sha256:7f2675750c50151f806070ec11258edf4c328340916c53bac0adbc465abd6b1e",
+                "sha256:960d7f42277391e8b1c0b0ae427a214e1b31a1278de6b73f8807b20c2e913bba",
+                "sha256:a50b0888d8a021a3342d36a6086501e30de7d840ab68fca44913e97d14487dc1",
+                "sha256:b7dbc5e8c39ea3ad3db22715f1b5401cd698a621218680c6daf42c2f9d36e205",
+                "sha256:bb3d29df5d07d5399d58a394d0ef50adf303ab4fbf66dfd25b9ef258effcb692",
+                "sha256:c0fff2733f7c2950f58a4fd09b5db257b00c6fec57bf3f68c5bae004d804b407",
+                "sha256:c792d3707a86c01c02607ae74364854220fb3e82735f631cd0a345dea6b4cee5",
+                "sha256:c90bda74e16bcd03861b09b1d37c0a4158feda5d5a036bb2d6e58de6ff65793e",
+                "sha256:cfce79ce41cc1a1dc7fc85bb41eeeb32d34a4cf39a645c717c0550287e30ff06",
+                "sha256:eeafb646f374988c22c8e6da5ab9fb81367ecfe81c70c292623373d2a021b1a1",
+                "sha256:f425f50a6dd807cb9043d15a4fcfba3b5874a54d9587ccbb748899f70dc18c47",
+                "sha256:fcd4459fe35a400b8f416bc57906862693c9f88b66dc925e7f2a933e77f6b18b",
+                "sha256:ff3936dd5feaefb4f91c8c1f50a06c588b5dc69fba4f7d9c79a6617ad80bb7df"
+            ],
+            "version": "==5.0.1"
+        },
+        "cryptography": {
+            "hashes": [
+                "sha256:02079a6addc7b5140ba0825f542c0869ff4df9a69c360e339ecead5baefa843c",
+                "sha256:1df22371fbf2004c6f64e927668734070a8953362cd8370ddd336774d6743595",
+                "sha256:369d2346db5934345787451504853ad9d342d7f721ae82d098083e1f49a582ad",
+                "sha256:3cda1f0ed8747339bbdf71b9f38ca74c7b592f24f65cdb3ab3765e4b02871651",
+                "sha256:44ff04138935882fef7c686878e1c8fd80a723161ad6a98da31e14b7553170c2",
+                "sha256:4b1030728872c59687badcca1e225a9103440e467c17d6d1730ab3d2d64bfeff",
+                "sha256:58363dbd966afb4f89b3b11dfb8ff200058fbc3b947507675c19ceb46104b48d",
+                "sha256:6ec280fb24d27e3d97aa731e16207d58bd8ae94ef6eab97249a2afe4ba643d42",
+                "sha256:7270a6c29199adc1297776937a05b59720e8a782531f1f122f2eb8467f9aab4d",
+                "sha256:73fd30c57fa2d0a1d7a49c561c40c2f79c7d6c374cc7750e9ac7c99176f6428e",
+                "sha256:7f09806ed4fbea8f51585231ba742b58cbcfbfe823ea197d8c89a5e433c7e912",
+                "sha256:90df0cc93e1f8d2fba8365fb59a858f51a11a394d64dbf3ef844f783844cc793",
+                "sha256:971221ed40f058f5662a604bd1ae6e4521d84e6cad0b7b170564cc34169c8f13",
+                "sha256:a518c153a2b5ed6b8cc03f7ae79d5ffad7315ad4569b2d5333a13c38d64bd8d7",
+                "sha256:b0de590a8b0979649ebeef8bb9f54394d3a41f66c5584fff4220901739b6b2f0",
+                "sha256:b43f53f29816ba1db8525f006fa6f49292e9b029554b3eb56a189a70f2a40879",
+                "sha256:d31402aad60ed889c7e57934a03477b572a03af7794fa8fb1780f21ea8f6551f",
+                "sha256:de96157ec73458a7f14e3d26f17f8128c959084931e8997b9e655a39c8fde9f9",
+                "sha256:df6b4dca2e11865e6cfbfb708e800efb18370f5a46fd601d3755bc7f85b3a8a2",
+                "sha256:ecadccc7ba52193963c0475ac9f6fa28ac01e01349a2ca48509667ef41ffd2cf",
+                "sha256:fb81c17e0ebe3358486cd8cc3ad78adbae58af12fc2bf2bc0bb84e8090fa5ce8"
+            ],
+            "version": "==2.8"
         },
         "decorator": {
             "hashes": [
@@ -613,10 +680,10 @@
         },
         "identify": {
             "hashes": [
-                "sha256:7782115794ec28b011702815d9f5e532244560cd2bf0789c4f09381d43befd90",
-                "sha256:9e7521e9abeaede4d2d1092a106e418c65ddf6b3182b43930bcb3c8cfb974488"
+                "sha256:6f44e637caa40d1b4cb37f6ed3b262ede74901d28b1cc5b1fc07360871edd65d",
+                "sha256:72e9c4ed3bc713c7045b762b0d2e2115c572b85abfc1f4604f5a4fd4c6642b71"
             ],
-            "version": "==1.4.8"
+            "version": "==1.4.9"
         },
         "idna": {
             "hashes": [
@@ -627,26 +694,18 @@
         },
         "imagesize": {
             "hashes": [
-                "sha256:3f349de3eb99145973fefb7dbe38554414e5c30abd0c8e4b970a7c9d09f3a1d8",
-                "sha256:f3832918bc3c66617f92e35f5d70729187676313caa60c187eb0f28b8fe5e3b5"
+                "sha256:6965f19a6a2039c7d48bca7dba2473069ff854c36ae6f19d2cde309d998228a1",
+                "sha256:b1f6b5a4eab1f73479a50fb79fcf729514a900c341d8503d62a62dbc4127a2b1"
             ],
-            "version": "==1.1.0"
-        },
-        "importlib-metadata": {
-            "hashes": [
-                "sha256:073a852570f92da5f744a3472af1b61e28e9f78ccf0c9117658dc32b15de7b45",
-                "sha256:d95141fbfa7ef2ec65cfd945e2af7e5a6ddbd7c8d9a25e66ff3be8e3daf9f60f"
-            ],
-            "markers": "python_version < '3.8'",
-            "version": "==1.3.0"
+            "version": "==1.2.0"
         },
         "ipython": {
             "hashes": [
-                "sha256:190a279bd3d4fc585a611e9358a88f1048cc57fd688254a86f9461889ee152a6",
-                "sha256:762d79a62b6aa96b04971e920543f558dfbeedc0468b899303c080c8068d4ac2"
+                "sha256:0f4bcf18293fb666df8511feec0403bdb7e061a5842ea6e88a3177b0ceb34ead",
+                "sha256:387686dd7fc9caf29d2fddcf3116c4b07a11d9025701d220c589a430b0171d8a"
             ],
             "index": "pypi",
-            "version": "==7.10.2"
+            "version": "==7.11.1"
         },
         "ipython-genutils": {
             "hashes": [
@@ -665,10 +724,17 @@
         },
         "jedi": {
             "hashes": [
-                "sha256:786b6c3d80e2f06fd77162a07fed81b8baa22dde5d62896a790a331d6ac21a27",
-                "sha256:ba859c74fa3c966a22f2aeebe1b74ee27e2a462f56d3f5f7ca4a59af61bfe42e"
+                "sha256:1349c1e8c107095a55386628bb3b2a79422f3a2cab8381e34ce19909e0cf5064",
+                "sha256:e909527104a903606dd63bea6e8e888833f0ef087057829b89a18364a856f807"
+            ],
+            "version": "==0.15.2"
+        },
+        "jeepney": {
+            "hashes": [
+                "sha256:13806f91a96e9b2623fd2a81b950d763ee471454aafd9eb6d75dbe7afce428fb",
+                "sha256:f6a3f93464a0cf052f4e87da3c8b3ed1e27696758fb9739c63d3a74d9a1b6774"
             ],
-            "version": "==0.15.1"
+            "version": "==0.4.1"
         },
         "jinja2": {
             "hashes": [
@@ -679,10 +745,10 @@
         },
         "keyring": {
             "hashes": [
-                "sha256:a3f71fc0cf6b74e201e70532879ba1d15db25cb2c7407dce52fe52a6d5fc7b66",
-                "sha256:fc9cadedae35b77141f670f84c10a657147d2e526348698c93dd77f039979729"
+                "sha256:5f5f92327b6c7432bebc18a1b60cb3797d99b08db1f5b919b8187c37a01f1ccc",
+                "sha256:ad84f7fe26ab51731f089eaf1c44ebf4c5fae323653c908888a3a6212ad0bbe7"
             ],
-            "version": "==20.0.0"
+            "version": "==21.0.0"
         },
         "lazy-object-proxy": {
             "hashes": [
@@ -766,23 +832,23 @@
         },
         "mypy": {
             "hashes": [
-                "sha256:02d9bdd3398b636723ecb6c5cfe9773025a9ab7f34612c1cde5c7f2292e2d768",
-                "sha256:088f758a50af31cf8b42688118077292370c90c89232c783ba7979f39ea16646",
-                "sha256:28e9fbc96d13397a7ddb7fad7b14f373f91b5cff538e0772e77c270468df083c",
-                "sha256:30e123b24931f02c5d99307406658ac8f9cd6746f0d45a3dcac2fe5fbdd60939",
-                "sha256:3294821b5840d51a3cd7a2bb63b40fc3f901f6a3cfb3c6046570749c4c7ef279",
-                "sha256:41696a7d912ce16fdc7c141d87e8db5144d4be664a0c699a2b417d393994b0c2",
-                "sha256:4f42675fa278f3913340bb8c3371d191319704437758d7c4a8440346c293ecb2",
-                "sha256:54d205ccce6ed930a8a2ccf48404896d456e8b87812e491cb907a355b1a9c640",
-                "sha256:6992133c95a2847d309b4b0c899d7054adc60481df6f6b52bb7dee3d5fd157f7",
-                "sha256:6ecbd0e8e371333027abca0922b0c2c632a5b4739a0c61ffbd0733391e39144c",
-                "sha256:83fa87f556e60782c0fc3df1b37b7b4a840314ba1ac27f3e1a1e10cb37c89c17",
-                "sha256:c87ac7233c629f305602f563db07f5221950fe34fe30af072ac838fa85395f78",
-                "sha256:de9ec8dba773b78c49e7bec9a35c9b6fc5235682ad1fc2105752ae7c22f4b931",
-                "sha256:f385a0accf353ca1bca4bbf473b9d83ed18d923fdb809d3a70a385da23e25b6a"
+                "sha256:0a9a45157e532da06fe56adcfef8a74629566b607fa2c1ac0122d1ff995c748a",
+                "sha256:2c35cae79ceb20d47facfad51f952df16c2ae9f45db6cb38405a3da1cf8fc0a7",
+                "sha256:4b9365ade157794cef9685791032521233729cb00ce76b0ddc78749abea463d2",
+                "sha256:53ea810ae3f83f9c9b452582261ea859828a9ed666f2e1ca840300b69322c474",
+                "sha256:634aef60b4ff0f650d3e59d4374626ca6153fcaff96ec075b215b568e6ee3cb0",
+                "sha256:7e396ce53cacd5596ff6d191b47ab0ea18f8e0ec04e15d69728d530e86d4c217",
+                "sha256:7eadc91af8270455e0d73565b8964da1642fe226665dd5c9560067cd64d56749",
+                "sha256:7f672d02fffcbace4db2b05369142e0506cdcde20cea0e07c7c2171c4fd11dd6",
+                "sha256:85baab8d74ec601e86134afe2bcccd87820f79d2f8d5798c889507d1088287bf",
+                "sha256:87c556fb85d709dacd4b4cb6167eecc5bbb4f0a9864b69136a0d4640fdc76a36",
+                "sha256:a6bd44efee4dc8c3324c13785a9dc3519b3ee3a92cada42d2b57762b7053b49b",
+                "sha256:c6d27bd20c3ba60d5b02f20bd28e20091d6286a699174dfad515636cb09b5a72",
+                "sha256:e2bb577d10d09a2d8822a042a23b8d62bc3b269667c9eb8e60a6edfa000211b1",
+                "sha256:f97a605d7c8bc2c6d1172c2f0d5a65b24142e11a58de689046e62c2d632ca8c1"
             ],
             "index": "pypi",
-            "version": "==0.750"
+            "version": "==0.761"
         },
         "mypy-extensions": {
             "hashes": [
@@ -813,9 +879,10 @@
         },
         "pathspec": {
             "hashes": [
-                "sha256:e285ccc8b0785beadd4c18e5708b12bb8fcf529a1e61215b3feff1d1e559ea5c"
+                "sha256:163b0632d4e31cef212976cf57b43d9fd6b0bac6e67c26015d611a647d5e7424",
+                "sha256:562aa70af2e0d434367d9790ad37aed893de47f1693e4201fd1d3dca15d19b96"
             ],
-            "version": "==0.6.0"
+            "version": "==0.7.0"
         },
         "pexpect": {
             "hashes": [
@@ -857,11 +924,11 @@
         },
         "pre-commit": {
             "hashes": [
-                "sha256:9f152687127ec90642a2cc3e4d9e1e6240c4eb153615cb02aa1ad41d331cbb6e",
-                "sha256:c2e4810d2d3102d354947907514a78c5d30424d299dc0fe48f5aa049826e9b50"
+                "sha256:8f48d8637bdae6fa70cc97db9c1dd5aa7c5c8bf71968932a380628c25978b850",
+                "sha256:f92a359477f3252452ae2e8d3029de77aec59415c16ae4189bcfba40b757e029"
             ],
             "index": "pypi",
-            "version": "==1.20.0"
+            "version": "==1.21.0"
         },
         "prompt-toolkit": {
             "hashes": [
@@ -879,10 +946,16 @@
         },
         "py": {
             "hashes": [
-                "sha256:64f65755aee5b381cea27766a3a147c3f15b9b6b9ac88676de66ba2ae36793fa",
-                "sha256:dc639b046a6e2cff5bbe40194ad65936d6ba360b52b3c3fe1d08a82dd50b5e53"
+                "sha256:5e27081401262157467ad6e7f851b7aa402c5852dbcb3dae06768434de5752aa",
+                "sha256:c20fdd83a5dbc0af9efd622bee9a5564e278f6380fffcacc43ba6f43db2813b0"
+            ],
+            "version": "==1.8.1"
+        },
+        "pycparser": {
+            "hashes": [
+                "sha256:a988718abfad80b6b157acce7bf130a30876d27603738ac39f140993246b25b3"
             ],
-            "version": "==1.8.0"
+            "version": "==2.19"
         },
         "pydocstyle": {
             "hashes": [
@@ -909,10 +982,10 @@
         },
         "pyparsing": {
             "hashes": [
-                "sha256:20f995ecd72f2a1f4bf6b072b63b22e2eb457836601e76d6e5dfcd75436acc1f",
-                "sha256:4ca62001be367f01bd3e92ecbb79070272a9d4964dce6a48a82ff0b8bc7e683a"
+                "sha256:4c830582a84fb022400b85429791bc551f1f4871c33f23e44f353119e92f969f",
+                "sha256:c342dccb5250c08d45fd6f8b4a559613ca603b57498511740e65cd11a2e7dcec"
             ],
-            "version": "==2.4.5"
+            "version": "==2.4.6"
         },
         "pytest": {
             "hashes": [
@@ -940,19 +1013,19 @@
         },
         "pyyaml": {
             "hashes": [
-                "sha256:0e7f69397d53155e55d10ff68fdfb2cf630a35e6daf65cf0bdeaf04f127c09dc",
-                "sha256:2e9f0b7c5914367b0916c3c104a024bb68f269a486b9d04a2e8ac6f6597b7803",
-                "sha256:35ace9b4147848cafac3db142795ee42deebe9d0dad885ce643928e88daebdcc",
-                "sha256:38a4f0d114101c58c0f3a88aeaa44d63efd588845c5a2df5290b73db8f246d15",
-                "sha256:483eb6a33b671408c8529106df3707270bfacb2447bf8ad856a4b4f57f6e3075",
-                "sha256:4b6be5edb9f6bb73680f5bf4ee08ff25416d1400fbd4535fe0069b2994da07cd",
-                "sha256:7f38e35c00e160db592091751d385cd7b3046d6d51f578b29943225178257b31",
-                "sha256:8100c896ecb361794d8bfdb9c11fce618c7cf83d624d73d5ab38aef3bc82d43f",
-                "sha256:c0ee8eca2c582d29c3c2ec6e2c4f703d1b7f1fb10bc72317355a746057e7346c",
-                "sha256:e4c015484ff0ff197564917b4b4246ca03f411b9bd7f16e02a2f586eb48b6d04",
-                "sha256:ebc4ed52dcc93eeebeae5cf5deb2ae4347b3a81c3fa12b0b8c976544829396a4"
+                "sha256:21a8e19e2007a4047ffabbd8f0ee32c0dabae3b7f4b6c645110ae53e7714b470",
+                "sha256:74ad685bfb065f4bdd36d24aa97092f04bcbb1179b5ffdd3d5f994023fb8c292",
+                "sha256:79c3ba1da22e61c2a71aaa382c57518ab492278c8974c40187b900b50f3e0282",
+                "sha256:94ad913ab3fd967d14ecffda8182d7d0e1f7dd919b352773c492ec51890d3224",
+                "sha256:998db501e3a627c3e5678d6505f0e182d1529545df289db036cdc717f35d8058",
+                "sha256:9b69d4645bff5820713e8912bc61c4277dc127a6f8c197b52b6436503c42600f",
+                "sha256:9da13b536533518343a04f3c6564782ec8a13c705310b26b4832d77fa4d92a47",
+                "sha256:a76159f13b47fb44fb2acac8fef798a1940dd31b4acec6f4560bd11b2d92d31b",
+                "sha256:a9e9175c1e47a089a2b45d9e2afc6aae1f1f725538c32eec761894a42ba1227f",
+                "sha256:ea51ce7b96646ecd3bb12c2702e570c2bd7dd4d9f146db7fa83c5008ede35f66",
+                "sha256:ffbaaa05de60fc444eda3f6300d1af27d965b09b67f1fb4ebcc88dd0fb4ab1b4"
             ],
-            "version": "==5.2"
+            "version": "==5.3b1"
         },
         "readme-renderer": {
             "hashes": [
@@ -971,19 +1044,29 @@
         },
         "regex": {
             "hashes": [
-                "sha256:3dbd8333fd2ebd50977ac8747385a73aa1f546eb6b16fcd83d274470fe11f243",
-                "sha256:40b7d1291a56897927e08bb973f8c186c2feb14c7f708bfe7aaee09483e85a20",
-                "sha256:719978a9145d59fc78509ea1d1bb74243f93583ef2a34dcc5623cf8118ae9726",
-                "sha256:75cf3796f89f75f83207a5c6a6e14eaf57e0369ef0ffff8e22bf36bbcfa0f1de",
-                "sha256:77396cf80be8b2a35db863cca4c1a902d88ceeb183adab328b81184e71a5eafe",
-                "sha256:77a3799152951d6d14ae5720ca162c97c64f85d4755da585418eac216b736cad",
-                "sha256:91235c98283d2bddf1a588f0fbc2da8afa37959294bbd18b76297bdf316ba4d6",
-                "sha256:aaffd68c4c1ed891366d5c390081f4bf6337595e76a157baf453603d8e53fbcb",
-                "sha256:ad9e3c7260809c0d1ded100269f78ea0217c0704f1eaaf40a382008461848b45",
-                "sha256:c203c9ee755e9656d0af8fab82754d5a664ebaf707b3f883c7eff6a3dd5151cf",
-                "sha256:e865bc508e316a3a09d36c8621596e6599a203bc54f1cd41020a127ccdac468a"
-            ],
-            "version": "==2019.12.9"
+                "sha256:032fdcc03406e1a6485ec09b826eac78732943840c4b29e503b789716f051d8d",
+                "sha256:0e6cf1e747f383f52a0964452658c04300a9a01e8a89c55ea22813931b580aa8",
+                "sha256:106e25a841921d8259dcef2a42786caae35bc750fb996f830065b3dfaa67b77e",
+                "sha256:1768cf42a78a11dae63152685e7a1d90af7a8d71d2d4f6d2387edea53a9e0588",
+                "sha256:27d1bd20d334f50b7ef078eba0f0756a640fd25f5f1708d3b5bed18a5d6bced9",
+                "sha256:29b20f66f2e044aafba86ecf10a84e611b4667643c42baa004247f5dfef4f90b",
+                "sha256:4850c78b53acf664a6578bba0e9ebeaf2807bb476c14ec7e0f936f2015133cae",
+                "sha256:57eacd38a5ec40ed7b19a968a9d01c0d977bda55664210be713e750dd7b33540",
+                "sha256:724eb24b92fc5fdc1501a1b4df44a68b9c1dda171c8ef8736799e903fb100f63",
+                "sha256:77ae8d926f38700432807ba293d768ba9e7652df0cbe76df2843b12f80f68885",
+                "sha256:78b3712ec529b2a71731fbb10b907b54d9c53a17ca589b42a578bc1e9a2c82ea",
+                "sha256:7bbbdbada3078dc360d4692a9b28479f569db7fc7f304b668787afc9feb38ec8",
+                "sha256:8d9ef7f6c403e35e73b7fc3cde9f6decdc43b1cb2ff8d058c53b9084bfcb553e",
+                "sha256:a83049eb717ae828ced9cf607845929efcb086a001fc8af93ff15c50012a5716",
+                "sha256:adc35d38952e688535980ae2109cad3a109520033642e759f987cf47fe278aa1",
+                "sha256:c29a77ad4463f71a506515d9ec3a899ed026b4b015bf43245c919ff36275444b",
+                "sha256:cfd31b3300fefa5eecb2fe596c6dee1b91b3a05ece9d5cfd2631afebf6c6fadd",
+                "sha256:d3ee0b035816e0520fac928de31b6572106f0d75597f6fa3206969a02baba06f",
+                "sha256:d508875793efdf6bab3d47850df8f40d4040ae9928d9d80864c1768d6aeaf8e3",
+                "sha256:ef0b828a7e22e58e06a1cceddba7b4665c6af8afeb22a0d8083001330572c147",
+                "sha256:faad39fdbe2c2ccda9846cd21581063086330efafa47d87afea4073a08128656"
+            ],
+            "version": "==2019.12.20"
         },
         "requests": {
             "hashes": [
@@ -1009,13 +1092,21 @@
             "index": "pypi",
             "version": "==0.14.0"
         },
+        "secretstorage": {
+            "hashes": [
+                "sha256:20c797ae48a4419f66f8d28fc221623f11fc45b6828f96bdb1ad9990acb59f92",
+                "sha256:7a119fb52a88e398dbb22a4b3eb39b779bfbace7e4153b7bc6e5954d86282a8a"
+            ],
+            "markers": "sys_platform == 'linux'",
+            "version": "==3.1.1"
+        },
         "seed-isort-config": {
             "hashes": [
-                "sha256:3e7c194d6322bfd15827640b85ddefbfcb205407530ebff5298700ae88d2397d",
-                "sha256:6805a6567f30bb1b799c5c90d29789ea5b8bb6261f31ea745095ebc5eac6b027"
+                "sha256:81f7adf8dd0bc24e03d0f5eef87561e170757fc7a36050c01e4c29d150757783",
+                "sha256:cecccb911b8d01ecdca16e17d083bf4cd78e928db4d7906125b0373b81f1c7ec"
             ],
             "index": "pypi",
-            "version": "==1.9.3"
+            "version": "==1.9.4"
         },
         "six": {
             "hashes": [
@@ -1034,11 +1125,11 @@
         },
         "sphinx": {
             "hashes": [
-                "sha256:0a11e2fd31fe5c7e64b4fc53c2c022946512f021d603eb41ac6ae51d5fcbb574",
-                "sha256:138e39aa10f28d52aa5759fc6d1cba2be6a4b750010974047fa7d0e31addcf63"
+                "sha256:298537cb3234578b2d954ff18c5608468229e116a9757af3b831c2b2b4819159",
+                "sha256:e6e766b74f85f37a5f3e0773a1e1be8db3fcb799deb58ca6d18b70b0b44542a5"
             ],
             "index": "pypi",
-            "version": "==2.3.0"
+            "version": "==2.3.1"
         },
         "sphinx-markdown-tables": {
             "hashes": [
@@ -1107,10 +1198,10 @@
         },
         "tqdm": {
             "hashes": [
-                "sha256:7543892c59720e36e4212180274d8f58dde36803bc1f6370fd09afa20b8f5892",
-                "sha256:f0ab01cf3ae5673d18f918700c0165e5fad0f26b5ebe4b34f62ead92686b5340"
+                "sha256:4789ccbb6fc122b5a6a85d512e4e41fc5acad77216533a6f2b8ce51e0f265c23",
+                "sha256:efab950cf7cc1e4d8ee50b2bb9c8e4a89f8307b49e0b2c9cfef3ec4ca26655eb"
             ],
-            "version": "==4.40.2"
+            "version": "==4.41.1"
         },
         "traitlets": {
             "hashes": [
@@ -1150,7 +1241,6 @@
                 "sha256:fdc1c9bbf79510b76408840e009ed65958feba92a88833cdceecff93ae8fff66",
                 "sha256:ffde2fbfad571af120fcbfbbc61c72469e72f550d676c3342492a9dfdefb8f12"
             ],
-            "markers": "implementation_name == 'cpython' and python_version < '3.8'",
             "version": "==1.4.0"
         },
         "typing-extensions": {
@@ -1184,10 +1274,9 @@
         },
         "wcwidth": {
             "hashes": [
-                "sha256:3df37372226d6e63e1b1e1eda15c594bca98a22d33a23832a90998faa96bc65e",
-                "sha256:f4ebe71925af7b40a864553f761ed559b43544f8f71746c2d756c7fe788ade7c"
+                "sha256:8fd29383f539be45b20bd4df0dc29c20ba48654a41e661925e612311e9f3c603"
             ],
-            "version": "==0.1.7"
+            "version": "==0.1.8"
         },
         "webencodings": {
             "hashes": [
@@ -1201,13 +1290,6 @@
                 "sha256:565a021fd19419476b9362b05eeaa094178de64f8361e44468f9e9d7843901e1"
             ],
             "version": "==1.11.2"
-        },
-        "zipp": {
-            "hashes": [
-                "sha256:3718b1cbcd963c7d4c5511a8240812904164b7f381b647143a89d3b98f9bcd8e",
-                "sha256:f06903e9f1f43b12d371004b4ac7b06ab39a44adc747266928ae6debfa7b3335"
-            ],
-            "version": "==0.6.0"
         }
     }
 }
diff --git a/README.md b/README.md
index e6d887f49ede8568393c1cc133d528d09d45404e..b8479b6c2f40db5cceba646f552c36b0f56f1da8 100644
--- a/README.md
+++ b/README.md
@@ -93,4 +93,4 @@ pre-commit install
 ```
 
 ----
-(c) 2019, LasNQ @ Uni Hamburg
+(c) 2020, LasNQ @ Uni Hamburg
diff --git a/doc/conf.py b/doc/conf.py
index 34a3825c7e4cc6322a68a63f4cae1295fec16f1c..2696f53d6e2709837681b9dba9de42a35c560122 100644
--- a/doc/conf.py
+++ b/doc/conf.py
@@ -2,6 +2,7 @@
 #
 import re
 import sys
+from datetime import datetime
 
 # The theme to use for HTML and HTML Help pages.  See the documentation for
 # a list of builtin themes.
@@ -75,9 +76,9 @@ source_suffix = [".rst", ".md"]
 master_doc = "index"
 
 # General information about the project.
-project = u"openqlab"
-copyright = u"2017, LasNQ @ Uni Hamburg"
-author = u"LasNQ @ Uni Hamburg"
+project = "openqlab"
+copyright = f"{datetime.now().year}, LasNQ @ Uni Hamburg"
+author = "LasNQ @ Uni Hamburg"
 
 
 # The short X.Y version.
@@ -130,7 +131,7 @@ htmlhelp_basename = "openqlabdoc"
 
 # -- Options for LaTeX output ---------------------------------------------
 
-latex_elements = {
+latex_elements = {  # type: ignore
     # The paper size ('letterpaper' or 'a4paper').
     #
     # 'papersize': 'letterpaper',
@@ -149,14 +150,14 @@ latex_elements = {
 # (source start file, target name, title,
 #  author, documentclass [howto, manual, or own class]).
 latex_documents = [
-    (master_doc, "openqlab.tex", u"openqlab Documentation", u"LasNQ group", "manual")
+    (master_doc, "openqlab.tex", "openqlab Documentation", "LasNQ group", "manual")
 ]
 
 # -- Options for manual page output ---------------------------------------
 
 # One entry per manual page. List of tuples
 # (source start file, name, description, authors, manual section).
-man_pages = [(master_doc, "openqlab.tex", u"openqlab Documentation", [author], 1)]
+man_pages = [(master_doc, "openqlab.tex", "openqlab Documentation", [author], 1)]
 
 # -- Options for Texinfo output -------------------------------------------
 
@@ -167,7 +168,7 @@ texinfo_documents = [
     (
         master_doc,
         "openqlab.tex",
-        u"openqlab Documentation",
+        "openqlab Documentation",
         author,
         "openqlab",
         "Open source lab toolbox for quantum optical experiments.",
diff --git a/doc/conversion.rst b/doc/conversion.rst
new file mode 100644
index 0000000000000000000000000000000000000000..95683924a3e178c7fe0a534589cdc35d29a9f4e7
--- /dev/null
+++ b/doc/conversion.rst
@@ -0,0 +1,28 @@
+:mod:`openqlab.conversion` -- Data Conversions
+**********************************************
+
+dB
+--
+
+Decibel conversions.
+
+.. automodule:: openqlab.conversion.db
+    :members:
+
+
+Utils
+-----
+
+Some unit utils.
+
+.. automodule:: openqlab.conversion.utils
+    :members:
+
+
+Wavelength
+----------
+
+Wavelength calucations.
+
+.. automodule:: openqlab.conversion.wavelength
+    :members:
diff --git a/doc/index.rst b/doc/index.rst
index 85a5de22d00dee8ca9340478c14e46ea20c22c63..d30ba0e0d798865547aa9e700e183abac94914ce 100644
--- a/doc/index.rst
+++ b/doc/index.rst
@@ -24,29 +24,21 @@ Reference
    :maxdepth: 2
 
    analysis
+   conversion
    io
    plots
    datacontainer
 
-
-License
-=======
-
-Note that although this package might become free software at some point, it
-currently isn't because it is unclear who actually *owns* the software.
-Therefore, usage is only allowed within the Las-NQ group at the University of
-Hamburg, unless expressly stated otherwise.
-
 Contribute
 ==========
 
 Most of the original content in this package was written during the PhD theses of
 Sebastian Steinlechner and Tobias Gehring. It is currently maintained by
-Sebastian Steinlechner, Christian Darsow-Fromm and is looking for more
+Christian Darsow-Fromm and Jan Petermann and is looking for more
 volunteers who would like to contribute.
 
-If you want to contribute, feel free to do so. The source code is hosted on the
-gitlab server at AEI Hannover, https://gitlab.aei.uni-hannover.de/las-nq/lab/.
+If you want to contribute, feel free to do so. The source code is hosted on
+https://gitlab.rrz.uni-hamburg.de/las-nq/openqlab
 
 Indices and tables
 ==================
diff --git a/doc/usage.rst b/doc/usage.rst
index edec159df269e8f60b07b2ec3d73fca31852d60a..0953b6b151dabc02400d73fcafc88506f7a0f49c 100644
--- a/doc/usage.rst
+++ b/doc/usage.rst
@@ -48,10 +48,12 @@ automatically recognized, run
 Next Steps
 ----------
 
-To work with measurement data and do further analysis, here is some further reading for you:
+To work with measurement data and do further analysis, here is some further
+reading for you:
 
 .. toctree::
     :maxdepth: 2
 
     Working with DataFrames/DataContainer <dataframes>
     Designing a Servo <servodesign>
+    Visa Importer <visa_importer>
diff --git a/doc/visa_importer.md b/doc/visa_importer.md
new file mode 100644
index 0000000000000000000000000000000000000000..6599107301257870735e6a728afe1bfd8ebdeedc
--- /dev/null
+++ b/doc/visa_importer.md
@@ -0,0 +1,42 @@
+# Visa Importer
+
+Importing data directly from network attached device works via the `io.read` method.
+
+Currently there are the following devices implemented:
+
+- Keysight oscilloscope (LAN)
+- HP4395a (GPIB)
+
+Feel free to add more devices.
+
+```python
+In  [0]: from openqlab import io
+
+In  [1]: data = io.read("TCPIP::hostname_or_ip::INSTR")
+
+In  [2]: data.head()
+Out [2]:
+------------------------------------------------------------
+xincrement : 8.064e-07
+xreference : 0
+average_count : 1
+xorigin : 0.00754
+yUnit : V
+points : 992
+type : normal
+xUnit : s
+------------------------------------------------------------                                                        
+                 1         2        3         4
+Time
+0.007540  0.000252 -0.004599 -1.09422  0.029460
+0.007541 -0.000150 -0.003795 -1.09422 -0.010741
+0.007542  0.000654 -0.003393 -1.09422 -0.010741
+0.007542  0.000654 -0.003393 -1.29523  0.009359
+0.007543  0.000654 -0.004197 -1.09422  0.009359
+```
+
+**Do not forget to save the data!**
+
+```python
+dc.to_csv("filename.csv")
+```
diff --git a/src/openqlab/io/__init__.py b/src/openqlab/io/__init__.py
index 7dbd31c9102022a8c1821a552d4cd7c1a139f220..7b8ecc492e1578b0ecb5c276d248115f3164c8e7 100644
--- a/src/openqlab/io/__init__.py
+++ b/src/openqlab/io/__init__.py
@@ -124,15 +124,15 @@ def read(
     else:
         selected_importers = BaseImporter.auto_importers()
 
+    data: List[DataContainer] = [
+        _import(data_file, selected_importers, **kwargs) for data_file in files_list
+    ]
+
     if append is True:
         axis = 0
     else:
         axis = 1
 
-    data: List[DataContainer] = [
-        _import(data_file, selected_importers, **kwargs) for data_file in files_list
-    ]
-
     if as_list:
         return data
     return DataContainer.concat(data, axis=axis)
diff --git a/src/openqlab/io/base_importer.py b/src/openqlab/io/base_importer.py
index 25d5e96f3bceb1bf78516353430421a285dd2676..33e2ad19345e2876b449196e552319379d1e689b 100644
--- a/src/openqlab/io/base_importer.py
+++ b/src/openqlab/io/base_importer.py
@@ -19,6 +19,8 @@ from typing import (
     cast,
 )
 
+import pyvisa
+
 from openqlab.io.data_container import DataContainer
 from openqlab.io.importers import utils
 
@@ -59,7 +61,42 @@ class BaseImporter(ABC):
 
 
 class VisaImporter(BaseImporter, ABC):
-    pass
+    """VisaImporter template."""
+
+    IDN_STARTS_WITH = cast(str, abstract_class_attribute)
+    IDN_STARTS_WITH.__doc__ = "Start of the device IDN"
+    ADDRESS_STARTS_WITH = cast(str, abstract_class_attribute)
+    ADDRESS_STARTS_WITH.__doc__ = "Mandatory start of the address"
+
+    def __init__(self, data: Union[str, IO, Path], inst=None):
+        if not isinstance(data, str):
+            raise utils.UnknownFileType(f"{self.NAME}: not a string")
+        if not data.lower().startswith(self.ADDRESS_STARTS_WITH):
+            raise utils.UnknownFileType(f"{self.NAME}: not a Visa address")
+
+        rm = pyvisa.ResourceManager("@py")
+        if inst is None:
+            self._inst = rm.open_resource(data)
+        else:
+            self._inst = inst
+
+        self._check_connection()
+
+    @property
+    def idn(self) -> str:
+        return self.query("*IDN?").strip()
+
+    def query(self, query: str) -> str:
+        return self._inst.query(query)
+
+    def write(self, command: str):
+        self._inst.write(command)
+
+    def _check_connection(self):
+        try:
+            assert self.idn.startswith(self.IDN_STARTS_WITH)
+        except (AssertionError, pyvisa.errors.VisaIOError):
+            raise utils.UnknownFileType(f"{self.NAME}: cannot open connection")
 
 
 class StreamImporter(BaseImporter, ABC):
diff --git a/src/openqlab/io/importers/hp4395a_gpib.py b/src/openqlab/io/importers/hp4395a_gpib.py
index 7d6a40e7c276698fb8fb6ed71a82e9639bb9d462..548893914e46fa29c0b07daf77500f5c34035868 100644
--- a/src/openqlab/io/importers/hp4395a_gpib.py
+++ b/src/openqlab/io/importers/hp4395a_gpib.py
@@ -38,9 +38,11 @@ class HP4395A_GPIB(VisaImporter):
 
     NAME = "HP4395A_GPIB"
     AUTOIMPORTER = False
+    ADDRESS_STARTS_WITH = ""
 
     def __init__(self, file):
         self.file = file
+        super().__init__()
 
     def read(self):
         serial_port, gpib_address = self.file.split("::")
diff --git a/src/openqlab/io/importers/keysight_visa.py b/src/openqlab/io/importers/keysight_visa.py
new file mode 100644
index 0000000000000000000000000000000000000000..952c4026d828546e0a1e37e41b85ca2763b93d3a
--- /dev/null
+++ b/src/openqlab/io/importers/keysight_visa.py
@@ -0,0 +1,110 @@
+from typing import List
+
+import numpy as np
+
+from openqlab.io.base_importer import VisaImporter
+from openqlab.io.data_container import DataContainer
+from openqlab.io.importers import utils
+
+
+class KeysightVisa(VisaImporter):
+    NAME = "KeysightVisa"
+    AUTOIMPORTER = True
+    IDN_STARTS_WITH: str = "KEYSIGHT TECHNOLOGIES,DSO-X"
+    ADDRESS_STARTS_WITH: str = "tcpip::"
+    MAX_COLUMNS = 4
+    NUMBER_OF_POINTS = 1000
+
+    def read(self):
+        data = self._read_data()
+        output = DataContainer.concat(data, axis=1)
+        output.index.name = "Time"
+        output.header = self._header
+
+        return output
+
+    def _read_data(self) -> List[np.ndarray]:
+        self.write(":WAVeform:POINTs:MODE NORMal")
+        self.write(f":WAVeform:POINts {self.NUMBER_OF_POINTS}")
+        self.write(":WAVeform:FORMat ASCII")
+
+        self.write(":STOP")
+
+        self._read_meta_data()
+
+        xorigin = self._header["xorigin"]
+        step = self._header["xincrement"]
+        points = self._header["points"]
+        # Using arange this way gives always the correct number of points
+        self._index = np.arange(points) * step + xorigin
+
+        data = []
+        for i in range(1, 1 + self.MAX_COLUMNS):
+            channel_active = self.query(f":CHANnel{i}:DISPlay?").strip()
+            if channel_active == "1":
+                data.append(DataContainer({i: self._read_column(i)}, index=self._index))
+
+        if not data:
+            raise utils.ImportFailed(
+                f"'{self.NAME}' importer: No active trace on the scope"
+            )
+
+        # TODO start only if stopped
+        self.write(":RUN")
+        return data
+
+    def _read_meta_data(self):
+        preamble = self.query("WAV:PREamble?").strip()
+        entries = preamble.split(",")
+
+        type_dict = {
+            0: "normal",
+            1: "peak detect",
+            2: "average",
+            3: "hresolution",
+        }
+
+        self._header = dict(
+            # format=entries[0],
+            type=type_dict[int(entries[1])],
+            points=int(entries[2]),
+            average_count=int(entries[3]),
+            xincrement=float(entries[4]),
+            xorigin=float(entries[5]),
+            xreference=int(entries[6]),
+            xUnit="s",
+            yUnit="V",
+            # yincrement=float(entries[7]),
+            # yorigin=float(entries[8]),
+            # yreference=int(entries[9]),
+        )
+
+    def _read_column(self, channel: int) -> np.ndarray:
+        """
+        The data looks like this:
+
+        #800000139 6.43216e-003, 9.24623e-003, 4.02010e-003, 1.28643e-002
+
+        The first digit (8) defines the number of digits for the following
+        number (00000139) which is the length of the data.
+        """
+        self.write(f":WAVeform:SOURce CHANnel{channel}")
+        raw_data = self.query("WAV:DATA?").strip()
+
+        if not raw_data or not raw_data[0] == "#":
+            raise utils.ImportFailed(f"{self.NAME}: The data does not start with #")
+
+        try:
+            n = int(raw_data[1])
+            n_digits = int(raw_data[2 : n + 2])
+
+            clipped_data = raw_data[n + 2 :]
+            assert (
+                len(clipped_data) == n_digits
+            ), f"len data: {len(clipped_data)}, n_digits: {n_digits}"
+
+            data = np.array(clipped_data.split(","), dtype=float)
+        except (ValueError, AssertionError):
+            raise utils.ImportFailed(f"{self.NAME}: Could not process the data")
+
+        return data
diff --git a/src/tests/test_io/test_importers/test_keysight_visa/mock_visa.py b/src/tests/test_io/test_importers/test_keysight_visa/mock_visa.py
new file mode 100644
index 0000000000000000000000000000000000000000..b551d40751a34ac54a163e753a152468057d219d
--- /dev/null
+++ b/src/tests/test_io/test_importers/test_keysight_visa/mock_visa.py
@@ -0,0 +1,59 @@
+from re import match
+from typing import List
+
+
+class MockVisa:
+    def __init__(self):
+        self.log = []
+        self.read_termination = ""
+        self.waveform_channel: str = None
+        self.waveform_points: int = None
+        self.waveform_format: str = None
+        self.channels_enabled: List[str] = ["0"] * 4
+        self.channel_data = {
+            "channel1": "#800000139 1.35683e-002,-1.19603e-002,-3.11608e-003, 6.33216e-003, 9.14623e-003, 7.13618e-003, 1.03523e-002, 3.11608e-003, 5.93015e-003, 1.19603e-002",
+            "channel2": "#800000139 2.35683e-002,-2.19603e-002,-4.11608e-003, 7.33216e-003, 9.14623e-003, 7.13618e-003, 1.03523e-002, 3.11608e-003, 5.93015e-003, 2.19603e-002",
+            "channel3": "#800000139 3.35683e-002,-3.19603e-002,-5.11608e-003, 6.33216e-003, 9.14623e-003, 7.13618e-003, 1.03523e-002, 3.11608e-003, 5.93015e-003, 3.19603e-002",
+            "channel4": "#800000139 4.35683e-002,-4.19603e-002,-7.11608e-003, 6.33216e-003, 9.14623e-003, 7.13618e-003, 1.03523e-002, 3.11608e-003, 5.93015e-003, 4.19603e-002",
+        }
+        self.idn = "KEYSIGHT TECHNOLOGIES,DSO-X 3024T,MY57452230,07.30.2019051434"
+
+    def open_resource(self, _: str):
+        return self
+
+    def write(self, command: str):
+        known_commands = [":run", ":stop", ":waveform:points:mode"]
+
+        self.log.append(f"write: {command}")
+        print(self.log)
+
+        commands: list = command.lower().strip().split(" ")
+        if commands[0] == ":waveform:source":
+            self.waveform_channel = commands[1]
+        elif commands[0] == ":waveform:points":
+            self.waveform_points = int(commands[1])
+        elif commands[0] == ":waveform:format":
+            self.waveform_points = commands[1]
+        elif commands[0] in known_commands:
+            pass
+        else:
+            raise ValueError(f"Unknown command: {command}")
+
+    def query(self, query: str):
+        self.log.append(f"query: {query}")
+        print(self.log)
+
+        query = query.lower().strip()
+
+        if query == "wav:preamble?":
+            return "+4,+0,+10,+1,+8.00000000E-005,-4.00000000E-004,+0,+1.57035200E-006,-1.00000000E-004,+32768"
+        if query == "wav:data?":
+            return self.channel_data[self.waveform_channel]
+        if query == "*idn?":
+            return self.idn
+        m = match(r":channel(.):display\?", query)
+        if m is not None:
+            chan = int(m.group(1))
+            return self.channels_enabled[chan - 1]
+
+        raise ValueError(f"Unknown query: {query}")
diff --git a/src/tests/test_io/test_importers/test_keysight_visa/test_keysight_visa.py b/src/tests/test_io/test_importers/test_keysight_visa/test_keysight_visa.py
new file mode 100644
index 0000000000000000000000000000000000000000..49f2f5b36f3ebfc1f975ec2eecce243b0afe07a5
--- /dev/null
+++ b/src/tests/test_io/test_importers/test_keysight_visa/test_keysight_visa.py
@@ -0,0 +1,91 @@
+import unittest
+from pathlib import Path
+
+from mock_visa import MockVisa
+from openqlab.io.importers.keysight_visa import KeysightVisa
+from openqlab.io.importers.utils import ImportFailed, UnknownFileType
+
+filedir = Path(__file__).parent
+
+
+class TestKeysightVisa(unittest.TestCase):
+    def setUp(self):
+        mock = MockVisa()
+        self.importer = KeysightVisa("TCPIP::mockaddress", inst=mock)
+
+    def test_idn(self):
+        self.assertEqual(
+            self.importer.idn,
+            "KEYSIGHT TECHNOLOGIES,DSO-X 3024T,MY57452230,07.30.2019051434",
+        )
+
+    def test_read_data(self):
+        self.importer._inst.channels_enabled = ["1"] * 4
+        dc = self.importer.read()
+        self.assertEqual(len(dc.columns), 4)
+        self.assertEqual(dc.index.name, "Time")
+
+    def test_index(self):
+        self.importer._inst.channels_enabled = ["1"] * 4
+        dc = self.importer.read()
+        self.assertAlmostEqual(dc.index[5], 0)
+
+    def test_missing_data(self):
+        self.importer._inst.channels_enabled[0] = "1"
+        self.importer._inst.channel_data["channel1"] = ""
+        with self.assertRaises(ImportFailed):
+            dc = self.importer.read()
+
+    def test_wrong_data(self):
+        self.importer._inst.channels_enabled[0] = "1"
+        self.importer._inst.channel_data["channel1"] = "031243"
+        with self.assertRaises(ImportFailed):
+            dc = self.importer.read()
+
+    def test_without_active_trace(self):
+        with self.assertRaises(ImportFailed):
+            dc = self.importer.read()
+
+    def test_no_number_on_second_place(self):
+        self.importer._inst.channels_enabled[1] = "1"
+        self.importer._inst.channel_data["channel2"] = "#x3284023"
+        with self.assertRaises(ImportFailed):
+            dc = self.importer.read()
+
+    def test_clipped_data(self):
+        self.importer._inst.channels_enabled[0] = "1"
+        self.importer._inst.channel_data[
+            "channel1"
+        ] = "#800000140 1.35683e-002,-1.19603e-002,-3.11608e-003, 6.33216e-003, 9.14623e-003, 7.13618e-003, 1.03523e-002, 3.11608e-003, 5.93015e-003, 1.19603e-002"
+        with self.assertRaises(ImportFailed):
+            dc = self.importer.read()
+
+    def test_data_values(self):
+        self.importer._inst.channels_enabled = ["1"] * 4
+        dc = self.importer.read()
+        print(dc)
+        self.assertEqual(dc[1].iloc[0], 1.35683e-2)
+        self.assertEqual(dc[2].iloc[0], 2.35683e-2)
+        self.assertEqual(dc[3].iloc[0], 3.35683e-2)
+        self.assertEqual(dc[4].iloc[0], 4.35683e-2)
+
+        self.assertEqual(dc[1].iloc[1], -1.19603e-2)
+        self.assertEqual(dc[2].iloc[1], -2.19603e-2)
+        self.assertEqual(dc[3].iloc[1], -3.19603e-2)
+        self.assertEqual(dc[4].iloc[1], -4.19603e-2)
+
+        self.assertEqual(dc[1].iloc[-1], 1.19603e-2)
+        self.assertEqual(dc[2].iloc[-1], 2.19603e-2)
+        self.assertEqual(dc[3].iloc[-1], 3.19603e-2)
+        self.assertEqual(dc[4].iloc[-1], 4.19603e-2)
+
+    def test_open_real_resource(self):
+        with self.assertRaises(ConnectionRefusedError):
+            self.importer = KeysightVisa("TCPIP::localhost::INSTR")
+
+    def test_wrong_device_idn(self):
+        mock = MockVisa()
+        importer = KeysightVisa("TCPIP::mockaddress", inst=mock)
+        importer._inst.idn = "Something wrong"
+        with self.assertRaises(UnknownFileType):
+            importer._check_connection()