From e874c2816f94a2a795aeab4dbde7bb7264f10e48 Mon Sep 17 00:00:00 2001 From: asbmails2 Date: Tue, 5 May 2020 02:03:24 -0300 Subject: [PATCH 01/12] Add test connection test and disconnection test, the first pass, but the second don't --- Pipfile | 2 + Pipfile.lock | 568 +++++++++++++++++---- temp.txt | 30 ++ tests/simulator/__init__.py | 0 tests/simulator/test_initial_connection.py | 43 ++ 5 files changed, 532 insertions(+), 111 deletions(-) create mode 100644 temp.txt create mode 100644 tests/simulator/__init__.py create mode 100644 tests/simulator/test_initial_connection.py diff --git a/Pipfile b/Pipfile index d2c7f2d..8460d4f 100644 --- a/Pipfile +++ b/Pipfile @@ -9,6 +9,8 @@ flake8 = "*" pytest-cov = "*" autopep8 = "*" codacy-coverage = "*" +jupyter = "*" +notebook = "*" [packages] simpy = "*" diff --git a/Pipfile.lock b/Pipfile.lock index ea45945..e5fe1aa 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "ad50307b34c77d0a5a3b9f195516c28419bc666eddfdb236b136fcf62167c1eb" + "sha256": "85441d2cf225f73ec49b9e489f6b56ff08b3e9558ecf2c7b2b848fdd620d3c60" }, "pipfile-spec": 6, "requires": { @@ -18,10 +18,10 @@ "default": { "certifi": { "hashes": [ - "sha256:017c25db2a153ce562900032d5bc68e9f191e44e9a0f762f373977de9df1fbb3", - "sha256:25b64c7da4cd7479594d035c08c2d809eb4aab3a26e5a990ea98cc450c320f1f" + "sha256:1d987a998c75633c40847cc966fcf5904906c920a7f17ef374f5aa4282abd304", + "sha256:51fcb31174be6e6664c5f69e3e1691a2d72a1a12e90f872cbdb1567eb47b6519" ], - "version": "==2019.11.28" + "version": "==2020.4.5.1" }, "chardet": { "hashes": [ @@ -32,76 +32,76 @@ }, "codecov": { "hashes": [ - "sha256:8ed8b7c6791010d359baed66f84f061bba5bd41174bf324c31311e8737602788", - "sha256:ae00d68e18d8a20e9c3288ba3875ae03db3a8e892115bf9b83ef20507732bed4" + "sha256:09fb045eb044a619cd2b9dacd7789ae8e322cb7f18196378579fd8d883e6b665", + "sha256:aeeefa3a03cac8a78e4f988e935b51a4689bb1f17f20d4e827807ee11135f845" ], "index": "pypi", - "version": "==2.0.15" + "version": "==2.0.22" }, "coverage": { "hashes": [ - "sha256:15cf13a6896048d6d947bf7d222f36e4809ab926894beb748fc9caa14605d9c3", - "sha256:1daa3eceed220f9fdb80d5ff950dd95112cd27f70d004c7918ca6dfc6c47054c", - "sha256:1e44a022500d944d42f94df76727ba3fc0a5c0b672c358b61067abb88caee7a0", - "sha256:25dbf1110d70bab68a74b4b9d74f30e99b177cde3388e07cc7272f2168bd1477", - "sha256:3230d1003eec018ad4a472d254991e34241e0bbd513e97a29727c7c2f637bd2a", - "sha256:3dbb72eaeea5763676a1a1efd9b427a048c97c39ed92e13336e726117d0b72bf", - "sha256:5012d3b8d5a500834783689a5d2292fe06ec75dc86ee1ccdad04b6f5bf231691", - "sha256:51bc7710b13a2ae0c726f69756cf7ffd4362f4ac36546e243136187cfcc8aa73", - "sha256:527b4f316e6bf7755082a783726da20671a0cc388b786a64417780b90565b987", - "sha256:722e4557c8039aad9592c6a4213db75da08c2cd9945320220634f637251c3894", - "sha256:76e2057e8ffba5472fd28a3a010431fd9e928885ff480cb278877c6e9943cc2e", - "sha256:77afca04240c40450c331fa796b3eab6f1e15c5ecf8bf2b8bee9706cd5452fef", - "sha256:7afad9835e7a651d3551eab18cbc0fdb888f0a6136169fbef0662d9cdc9987cf", - "sha256:9bea19ac2f08672636350f203db89382121c9c2ade85d945953ef3c8cf9d2a68", - "sha256:a8b8ac7876bc3598e43e2603f772d2353d9931709345ad6c1149009fd1bc81b8", - "sha256:b0840b45187699affd4c6588286d429cd79a99d509fe3de0f209594669bb0954", - "sha256:b26aaf69713e5674efbde4d728fb7124e429c9466aeaf5f4a7e9e699b12c9fe2", - "sha256:b63dd43f455ba878e5e9f80ba4f748c0a2156dde6e0e6e690310e24d6e8caf40", - "sha256:be18f4ae5a9e46edae3f329de2191747966a34a3d93046dbdf897319923923bc", - "sha256:c312e57847db2526bc92b9bfa78266bfbaabac3fdcd751df4d062cd4c23e46dc", - "sha256:c60097190fe9dc2b329a0eb03393e2e0829156a589bd732e70794c0dd804258e", - "sha256:c62a2143e1313944bf4a5ab34fd3b4be15367a02e9478b0ce800cb510e3bbb9d", - "sha256:cc1109f54a14d940b8512ee9f1c3975c181bbb200306c6d8b87d93376538782f", - "sha256:cd60f507c125ac0ad83f05803063bed27e50fa903b9c2cfee3f8a6867ca600fc", - "sha256:d513cc3db248e566e07a0da99c230aca3556d9b09ed02f420664e2da97eac301", - "sha256:d649dc0bcace6fcdb446ae02b98798a856593b19b637c1b9af8edadf2b150bea", - "sha256:d7008a6796095a79544f4da1ee49418901961c97ca9e9d44904205ff7d6aa8cb", - "sha256:da93027835164b8223e8e5af2cf902a4c80ed93cb0909417234f4a9df3bcd9af", - "sha256:e69215621707119c6baf99bda014a45b999d37602cb7043d943c76a59b05bf52", - "sha256:ea9525e0fef2de9208250d6c5aeeee0138921057cd67fcef90fbed49c4d62d37", - "sha256:fca1669d464f0c9831fd10be2eef6b86f5ebd76c724d1e0706ebdff86bb4adf0" - ], - "version": "==5.0.3" + "sha256:00f1d23f4336efc3b311ed0d807feb45098fc86dee1ca13b3d6768cdab187c8a", + "sha256:01333e1bd22c59713ba8a79f088b3955946e293114479bbfc2e37d522be03355", + "sha256:0cb4be7e784dcdc050fc58ef05b71aa8e89b7e6636b99967fadbdba694cf2b65", + "sha256:0e61d9803d5851849c24f78227939c701ced6704f337cad0a91e0972c51c1ee7", + "sha256:1601e480b9b99697a570cea7ef749e88123c04b92d84cedaa01e117436b4a0a9", + "sha256:2742c7515b9eb368718cd091bad1a1b44135cc72468c731302b3d641895b83d1", + "sha256:2d27a3f742c98e5c6b461ee6ef7287400a1956c11421eb574d843d9ec1f772f0", + "sha256:402e1744733df483b93abbf209283898e9f0d67470707e3c7516d84f48524f55", + "sha256:5c542d1e62eece33c306d66fe0a5c4f7f7b3c08fecc46ead86d7916684b36d6c", + "sha256:5f2294dbf7875b991c381e3d5af2bcc3494d836affa52b809c91697449d0eda6", + "sha256:6402bd2fdedabbdb63a316308142597534ea8e1895f4e7d8bf7476c5e8751fef", + "sha256:66460ab1599d3cf894bb6baee8c684788819b71a5dc1e8fa2ecc152e5d752019", + "sha256:782caea581a6e9ff75eccda79287daefd1d2631cc09d642b6ee2d6da21fc0a4e", + "sha256:79a3cfd6346ce6c13145731d39db47b7a7b859c0272f02cdb89a3bdcbae233a0", + "sha256:7a5bdad4edec57b5fb8dae7d3ee58622d626fd3a0be0dfceda162a7035885ecf", + "sha256:8fa0cbc7ecad630e5b0f4f35b0f6ad419246b02bc750de7ac66db92667996d24", + "sha256:a027ef0492ede1e03a8054e3c37b8def89a1e3c471482e9f046906ba4f2aafd2", + "sha256:a3f3654d5734a3ece152636aad89f58afc9213c6520062db3978239db122f03c", + "sha256:a82b92b04a23d3c8a581fc049228bafde988abacba397d57ce95fe95e0338ab4", + "sha256:acf3763ed01af8410fc36afea23707d4ea58ba7e86a8ee915dfb9ceff9ef69d0", + "sha256:adeb4c5b608574a3d647011af36f7586811a2c1197c861aedb548dd2453b41cd", + "sha256:b83835506dfc185a319031cf853fa4bb1b3974b1f913f5bb1a0f3d98bdcded04", + "sha256:bb28a7245de68bf29f6fb199545d072d1036a1917dca17a1e75bbb919e14ee8e", + "sha256:bf9cb9a9fd8891e7efd2d44deb24b86d647394b9705b744ff6f8261e6f29a730", + "sha256:c317eaf5ff46a34305b202e73404f55f7389ef834b8dbf4da09b9b9b37f76dd2", + "sha256:dbe8c6ae7534b5b024296464f387d57c13caa942f6d8e6e0346f27e509f0f768", + "sha256:de807ae933cfb7f0c7d9d981a053772452217df2bf38e7e6267c9cbf9545a796", + "sha256:dead2ddede4c7ba6cb3a721870f5141c97dc7d85a079edb4bd8d88c3ad5b20c7", + "sha256:dec5202bfe6f672d4511086e125db035a52b00f1648d6407cc8e526912c0353a", + "sha256:e1ea316102ea1e1770724db01998d1603ed921c54a86a2efcb03428d5417e489", + "sha256:f90bfc4ad18450c80b024036eaf91e4a246ae287701aaa88eaebebf150868052" + ], + "version": "==5.1" }, "idna": { "hashes": [ - "sha256:c357b3f628cf53ae2c4c05627ecc484553142ca23264e593d327bcde5e9c3407", - "sha256:ea8b7f6188e6fa117537c3df7da9fc686d485087abf6ac197f9c46432f7e4a3c" + "sha256:7588d1c14ae4c77d74036e8c22ff447b26d0fde8f007354fd48a7814db15b7cb", + "sha256:a068a21ceac8a4d63dbfd964670474107f541babbd2250d61922f029858365fa" ], - "version": "==2.8" + "version": "==2.9" }, "requests": { "hashes": [ - "sha256:11e007a8a2aa0323f5a921e9e6a2d7e4e67d9877e85773fba9ba6419025cbeb4", - "sha256:9cf5292fcd0f598c671cfc1e0d7d1a7f13bb8085e9a590f48c010551dc6c4b31" + "sha256:43999036bfa82904b6af1d99e4882b560e5e2c68e5c4b0aa03b655f3d7d73fee", + "sha256:b3f43d496c6daba4493e7c431722aeb7dbc6288f52a6e04e7b6023b0247817e6" ], - "version": "==2.22.0" + "version": "==2.23.0" }, "simpy": { "hashes": [ - "sha256:b8eb814789124719cbca15563fbdaf874bec8d4bc19ba633d8f7a3a20839b513", - "sha256:d09625b9b01f34242ae100fe6bdb6c9508da181a08419ab00cc2bd47c7ec0f43" + "sha256:6c90c105628a5b4c9c3ee822b45b9980c60d3126b99295b616bb72cd69b6a35e", + "sha256:b36542e2faab612f861c5ef4da17220ac1553f5892b3583c67281dbe4faad404" ], "index": "pypi", - "version": "==3.0.11" + "version": "==4.0.1" }, "urllib3": { "hashes": [ - "sha256:2f3db8b19923a873b3e5256dc9c2dedfa883e33d87c690d9c7913e1f40673cdc", - "sha256:87716c2d2a7121198ebcb7ce7cccf6ce5e9ba539041cfbaeecfb641dc0bf6acc" + "sha256:3018294ebefce6572a474f0604c2021e33b3fd8006ecd11d62107a5d2a963527", + "sha256:88206b0eb87e6d677d424843ac5209e3fb9d0190d0ee169599165ec25e9d9115" ], - "version": "==1.25.8" + "version": "==1.25.9" } }, "develop": { @@ -114,17 +114,31 @@ }, "autopep8": { "hashes": [ - "sha256:0f592a0447acea0c2b0a9602be1e4e3d86db52badd2e3c84f0193bfd89fd3a43" + "sha256:152fd8fe47d02082be86e05001ec23d6f420086db56b17fc883f3f965fb34954" ], "index": "pypi", - "version": "==1.5" + "version": "==1.5.2" + }, + "backcall": { + "hashes": [ + "sha256:38ecd85be2c1e78f77fd91700c76e14667dc21e2713b63876c0eb901196e01e4", + "sha256:bbbf4b1e5cd2bdb08f915895b51081c041bac22394fdfcfdfbe9f14b77c08bf2" + ], + "version": "==0.1.0" + }, + "bleach": { + "hashes": [ + "sha256:cc8da25076a1fe56c3ac63671e2194458e0c4d9c7becfd52ca251650d517903c", + "sha256:e78e426105ac07026ba098f04de8abe9b6e3e98b5befbf89b51a5ef0a4292b03" + ], + "version": "==3.1.4" }, "certifi": { "hashes": [ - "sha256:017c25db2a153ce562900032d5bc68e9f191e44e9a0f762f373977de9df1fbb3", - "sha256:25b64c7da4cd7479594d035c08c2d809eb4aab3a26e5a990ea98cc450c320f1f" + "sha256:1d987a998c75633c40847cc966fcf5904906c920a7f17ef374f5aa4282abd304", + "sha256:51fcb31174be6e6664c5f69e3e1691a2d72a1a12e90f872cbdb1567eb47b6519" ], - "version": "==2019.11.28" + "version": "==2020.4.5.1" }, "chardet": { "hashes": [ @@ -143,39 +157,53 @@ }, "coverage": { "hashes": [ - "sha256:15cf13a6896048d6d947bf7d222f36e4809ab926894beb748fc9caa14605d9c3", - "sha256:1daa3eceed220f9fdb80d5ff950dd95112cd27f70d004c7918ca6dfc6c47054c", - "sha256:1e44a022500d944d42f94df76727ba3fc0a5c0b672c358b61067abb88caee7a0", - "sha256:25dbf1110d70bab68a74b4b9d74f30e99b177cde3388e07cc7272f2168bd1477", - "sha256:3230d1003eec018ad4a472d254991e34241e0bbd513e97a29727c7c2f637bd2a", - "sha256:3dbb72eaeea5763676a1a1efd9b427a048c97c39ed92e13336e726117d0b72bf", - "sha256:5012d3b8d5a500834783689a5d2292fe06ec75dc86ee1ccdad04b6f5bf231691", - "sha256:51bc7710b13a2ae0c726f69756cf7ffd4362f4ac36546e243136187cfcc8aa73", - "sha256:527b4f316e6bf7755082a783726da20671a0cc388b786a64417780b90565b987", - "sha256:722e4557c8039aad9592c6a4213db75da08c2cd9945320220634f637251c3894", - "sha256:76e2057e8ffba5472fd28a3a010431fd9e928885ff480cb278877c6e9943cc2e", - "sha256:77afca04240c40450c331fa796b3eab6f1e15c5ecf8bf2b8bee9706cd5452fef", - "sha256:7afad9835e7a651d3551eab18cbc0fdb888f0a6136169fbef0662d9cdc9987cf", - "sha256:9bea19ac2f08672636350f203db89382121c9c2ade85d945953ef3c8cf9d2a68", - "sha256:a8b8ac7876bc3598e43e2603f772d2353d9931709345ad6c1149009fd1bc81b8", - "sha256:b0840b45187699affd4c6588286d429cd79a99d509fe3de0f209594669bb0954", - "sha256:b26aaf69713e5674efbde4d728fb7124e429c9466aeaf5f4a7e9e699b12c9fe2", - "sha256:b63dd43f455ba878e5e9f80ba4f748c0a2156dde6e0e6e690310e24d6e8caf40", - "sha256:be18f4ae5a9e46edae3f329de2191747966a34a3d93046dbdf897319923923bc", - "sha256:c312e57847db2526bc92b9bfa78266bfbaabac3fdcd751df4d062cd4c23e46dc", - "sha256:c60097190fe9dc2b329a0eb03393e2e0829156a589bd732e70794c0dd804258e", - "sha256:c62a2143e1313944bf4a5ab34fd3b4be15367a02e9478b0ce800cb510e3bbb9d", - "sha256:cc1109f54a14d940b8512ee9f1c3975c181bbb200306c6d8b87d93376538782f", - "sha256:cd60f507c125ac0ad83f05803063bed27e50fa903b9c2cfee3f8a6867ca600fc", - "sha256:d513cc3db248e566e07a0da99c230aca3556d9b09ed02f420664e2da97eac301", - "sha256:d649dc0bcace6fcdb446ae02b98798a856593b19b637c1b9af8edadf2b150bea", - "sha256:d7008a6796095a79544f4da1ee49418901961c97ca9e9d44904205ff7d6aa8cb", - "sha256:da93027835164b8223e8e5af2cf902a4c80ed93cb0909417234f4a9df3bcd9af", - "sha256:e69215621707119c6baf99bda014a45b999d37602cb7043d943c76a59b05bf52", - "sha256:ea9525e0fef2de9208250d6c5aeeee0138921057cd67fcef90fbed49c4d62d37", - "sha256:fca1669d464f0c9831fd10be2eef6b86f5ebd76c724d1e0706ebdff86bb4adf0" - ], - "version": "==5.0.3" + "sha256:00f1d23f4336efc3b311ed0d807feb45098fc86dee1ca13b3d6768cdab187c8a", + "sha256:01333e1bd22c59713ba8a79f088b3955946e293114479bbfc2e37d522be03355", + "sha256:0cb4be7e784dcdc050fc58ef05b71aa8e89b7e6636b99967fadbdba694cf2b65", + "sha256:0e61d9803d5851849c24f78227939c701ced6704f337cad0a91e0972c51c1ee7", + "sha256:1601e480b9b99697a570cea7ef749e88123c04b92d84cedaa01e117436b4a0a9", + "sha256:2742c7515b9eb368718cd091bad1a1b44135cc72468c731302b3d641895b83d1", + "sha256:2d27a3f742c98e5c6b461ee6ef7287400a1956c11421eb574d843d9ec1f772f0", + "sha256:402e1744733df483b93abbf209283898e9f0d67470707e3c7516d84f48524f55", + "sha256:5c542d1e62eece33c306d66fe0a5c4f7f7b3c08fecc46ead86d7916684b36d6c", + "sha256:5f2294dbf7875b991c381e3d5af2bcc3494d836affa52b809c91697449d0eda6", + "sha256:6402bd2fdedabbdb63a316308142597534ea8e1895f4e7d8bf7476c5e8751fef", + "sha256:66460ab1599d3cf894bb6baee8c684788819b71a5dc1e8fa2ecc152e5d752019", + "sha256:782caea581a6e9ff75eccda79287daefd1d2631cc09d642b6ee2d6da21fc0a4e", + "sha256:79a3cfd6346ce6c13145731d39db47b7a7b859c0272f02cdb89a3bdcbae233a0", + "sha256:7a5bdad4edec57b5fb8dae7d3ee58622d626fd3a0be0dfceda162a7035885ecf", + "sha256:8fa0cbc7ecad630e5b0f4f35b0f6ad419246b02bc750de7ac66db92667996d24", + "sha256:a027ef0492ede1e03a8054e3c37b8def89a1e3c471482e9f046906ba4f2aafd2", + "sha256:a3f3654d5734a3ece152636aad89f58afc9213c6520062db3978239db122f03c", + "sha256:a82b92b04a23d3c8a581fc049228bafde988abacba397d57ce95fe95e0338ab4", + "sha256:acf3763ed01af8410fc36afea23707d4ea58ba7e86a8ee915dfb9ceff9ef69d0", + "sha256:adeb4c5b608574a3d647011af36f7586811a2c1197c861aedb548dd2453b41cd", + "sha256:b83835506dfc185a319031cf853fa4bb1b3974b1f913f5bb1a0f3d98bdcded04", + "sha256:bb28a7245de68bf29f6fb199545d072d1036a1917dca17a1e75bbb919e14ee8e", + "sha256:bf9cb9a9fd8891e7efd2d44deb24b86d647394b9705b744ff6f8261e6f29a730", + "sha256:c317eaf5ff46a34305b202e73404f55f7389ef834b8dbf4da09b9b9b37f76dd2", + "sha256:dbe8c6ae7534b5b024296464f387d57c13caa942f6d8e6e0346f27e509f0f768", + "sha256:de807ae933cfb7f0c7d9d981a053772452217df2bf38e7e6267c9cbf9545a796", + "sha256:dead2ddede4c7ba6cb3a721870f5141c97dc7d85a079edb4bd8d88c3ad5b20c7", + "sha256:dec5202bfe6f672d4511086e125db035a52b00f1648d6407cc8e526912c0353a", + "sha256:e1ea316102ea1e1770724db01998d1603ed921c54a86a2efcb03428d5417e489", + "sha256:f90bfc4ad18450c80b024036eaf91e4a246ae287701aaa88eaebebf150868052" + ], + "version": "==5.1" + }, + "decorator": { + "hashes": [ + "sha256:41fa54c2a0cc4ba648be4fd43cff00aedf5b9465c9bf18d64325bc225f08f760", + "sha256:e3a62f0520172440ca0dcc823749319382e377f37f140a0b99ef45fecb84bfe7" + ], + "version": "==4.4.2" + }, + "defusedxml": { + "hashes": [ + "sha256:6687150770438374ab581bb7a1b327a847dd9c5749e396102de3fad4e8a3ef93", + "sha256:f684034d135af4c6cbb949b8a4d2ed61634515257a67299e5f940fbaa34377f5" + ], + "version": "==0.6.0" }, "entrypoints": { "hashes": [ @@ -194,10 +222,127 @@ }, "idna": { "hashes": [ - "sha256:c357b3f628cf53ae2c4c05627ecc484553142ca23264e593d327bcde5e9c3407", - "sha256:ea8b7f6188e6fa117537c3df7da9fc686d485087abf6ac197f9c46432f7e4a3c" + "sha256:7588d1c14ae4c77d74036e8c22ff447b26d0fde8f007354fd48a7814db15b7cb", + "sha256:a068a21ceac8a4d63dbfd964670474107f541babbd2250d61922f029858365fa" + ], + "version": "==2.9" + }, + "ipykernel": { + "hashes": [ + "sha256:003c9c1ab6ff87d11f531fee2b9ca59affab19676fc6b2c21da329aef6e73499", + "sha256:2937373c356fa5b634edb175c5ea0e4b25de8008f7c194f2d49cfbd1f9c970a8" + ], + "version": "==5.2.1" + }, + "ipython": { + "hashes": [ + "sha256:ca478e52ae1f88da0102360e57e528b92f3ae4316aabac80a2cd7f7ab2efb48a", + "sha256:eb8d075de37f678424527b5ef6ea23f7b80240ca031c2dd6de5879d687a65333" + ], + "version": "==7.13.0" + }, + "ipython-genutils": { + "hashes": [ + "sha256:72dd37233799e619666c9f639a9da83c34013a73e8bbc79a7a6348d93c61fab8", + "sha256:eb2e116e75ecef9d4d228fdc66af54269afa26ab4463042e33785b887c628ba8" + ], + "version": "==0.2.0" + }, + "ipywidgets": { + "hashes": [ + "sha256:13ffeca438e0c0f91ae583dc22f50379b9d6b28390ac7be8b757140e9a771516", + "sha256:e945f6e02854a74994c596d9db83444a1850c01648f1574adf144fbbabe05c97" + ], + "version": "==7.5.1" + }, + "jedi": { + "hashes": [ + "sha256:cd60c93b71944d628ccac47df9a60fec53150de53d42dc10a7fc4b5ba6aae798", + "sha256:df40c97641cb943661d2db4c33c2e1ff75d491189423249e989bcea4464f3030" + ], + "version": "==0.17.0" + }, + "jinja2": { + "hashes": [ + "sha256:89aab215427ef59c34ad58735269eb58b1a5808103067f7bb9d5836c651b3bb0", + "sha256:f0a4641d3cf955324a89c04f3d94663aa4d638abe8f733ecd3582848e1c37035" ], - "version": "==2.8" + "version": "==2.11.2" + }, + "jsonschema": { + "hashes": [ + "sha256:4e5b3cf8216f577bee9ce139cbe72eca3ea4f292ec60928ff24758ce626cd163", + "sha256:c8a85b28d377cc7737e46e2d9f2b4f44ee3c0e1deac6bf46ddefc7187d30797a" + ], + "version": "==3.2.0" + }, + "jupyter": { + "hashes": [ + "sha256:3e1f86076bbb7c8c207829390305a2b1fe836d471ed54be66a3b8c41e7f46cc7", + "sha256:5b290f93b98ffbc21c0c7e749f054b3267782166d72fa5e3ed1ed4eaf34a2b78", + "sha256:d9dc4b3318f310e34c82951ea5d6683f67bed7def4b259fafbfe4f1beb1d8e5f" + ], + "index": "pypi", + "version": "==1.0.0" + }, + "jupyter-client": { + "hashes": [ + "sha256:3a32fa4d0b16d1c626b30c3002a62dfd86d6863ed39eaba3f537fade197bb756", + "sha256:cde8e83aab3ec1c614f221ae54713a9a46d3bf28292609d2db1b439bef5a8c8e" + ], + "version": "==6.1.3" + }, + "jupyter-console": { + "hashes": [ + "sha256:6f6ead433b0534909df789ea64f0a14cdf9b6b2360757756f08182be4b9e431b", + "sha256:b392155112ec86a329df03b225749a0fa903aa80811e8eda55796a40b5e470d8" + ], + "version": "==6.1.0" + }, + "jupyter-core": { + "hashes": [ + "sha256:394fd5dd787e7c8861741880bdf8a00ce39f95de5d18e579c74b882522219e7e", + "sha256:a4ee613c060fe5697d913416fc9d553599c05e4492d58fac1192c9a6844abb21" + ], + "version": "==4.6.3" + }, + "markupsafe": { + "hashes": [ + "sha256:00bc623926325b26bb9605ae9eae8a215691f33cae5df11ca5424f06f2d1f473", + "sha256:09027a7803a62ca78792ad89403b1b7a73a01c8cb65909cd876f7fcebd79b161", + "sha256:09c4b7f37d6c648cb13f9230d847adf22f8171b1ccc4d5682398e77f40309235", + "sha256:1027c282dad077d0bae18be6794e6b6b8c91d58ed8a8d89a89d59693b9131db5", + "sha256:13d3144e1e340870b25e7b10b98d779608c02016d5184cfb9927a9f10c689f42", + "sha256:24982cc2533820871eba85ba648cd53d8623687ff11cbb805be4ff7b4c971aff", + "sha256:29872e92839765e546828bb7754a68c418d927cd064fd4708fab9fe9c8bb116b", + "sha256:43a55c2930bbc139570ac2452adf3d70cdbb3cfe5912c71cdce1c2c6bbd9c5d1", + "sha256:46c99d2de99945ec5cb54f23c8cd5689f6d7177305ebff350a58ce5f8de1669e", + "sha256:500d4957e52ddc3351cabf489e79c91c17f6e0899158447047588650b5e69183", + "sha256:535f6fc4d397c1563d08b88e485c3496cf5784e927af890fb3c3aac7f933ec66", + "sha256:596510de112c685489095da617b5bcbbac7dd6384aeebeda4df6025d0256a81b", + "sha256:62fe6c95e3ec8a7fad637b7f3d372c15ec1caa01ab47926cfdf7a75b40e0eac1", + "sha256:6788b695d50a51edb699cb55e35487e430fa21f1ed838122d722e0ff0ac5ba15", + "sha256:6dd73240d2af64df90aa7c4e7481e23825ea70af4b4922f8ede5b9e35f78a3b1", + "sha256:717ba8fe3ae9cc0006d7c451f0bb265ee07739daf76355d06366154ee68d221e", + "sha256:79855e1c5b8da654cf486b830bd42c06e8780cea587384cf6545b7d9ac013a0b", + "sha256:7c1699dfe0cf8ff607dbdcc1e9b9af1755371f92a68f706051cc8c37d447c905", + "sha256:88e5fcfb52ee7b911e8bb6d6aa2fd21fbecc674eadd44118a9cc3863f938e735", + "sha256:8defac2f2ccd6805ebf65f5eeb132adcf2ab57aa11fdf4c0dd5169a004710e7d", + "sha256:98c7086708b163d425c67c7a91bad6e466bb99d797aa64f965e9d25c12111a5e", + "sha256:9add70b36c5666a2ed02b43b335fe19002ee5235efd4b8a89bfcf9005bebac0d", + "sha256:9bf40443012702a1d2070043cb6291650a0841ece432556f784f004937f0f32c", + "sha256:ade5e387d2ad0d7ebf59146cc00c8044acbd863725f887353a10df825fc8ae21", + "sha256:b00c1de48212e4cc9603895652c5c410df699856a2853135b3967591e4beebc2", + "sha256:b1282f8c00509d99fef04d8ba936b156d419be841854fe901d8ae224c59f0be5", + "sha256:b2051432115498d3562c084a49bba65d97cf251f5a331c64a12ee7e04dacc51b", + "sha256:ba59edeaa2fc6114428f1637ffff42da1e311e29382d81b339c1817d37ec93c6", + "sha256:c8716a48d94b06bb3b2524c2b77e055fb313aeb4ea620c8dd03a105574ba704f", + "sha256:cd5df75523866410809ca100dc9681e301e3c27567cf498077e8551b6d20e42f", + "sha256:cdb132fc825c38e1aeec2c8aa9338310d29d337bebbd7baa06889d09a60a1fa2", + "sha256:e249096428b3ae81b08327a63a485ad0878de3fb939049038579ac0ef61e17e7", + "sha256:e8313f01ba26fbbe36c7be1966a7b7424942f670f38e666995b88d012765b9be" + ], + "version": "==1.1.1" }, "mccabe": { "hashes": [ @@ -206,6 +351,13 @@ ], "version": "==0.6.1" }, + "mistune": { + "hashes": [ + "sha256:59a3429db53c50b5c6bcc8a07f8848cb00d7dc8bdb431a4ab41920d201d4756e", + "sha256:88a1051873018da288eee8538d476dffe1262495144b33ecb586c4ab266bb8d4" + ], + "version": "==0.8.4" + }, "more-itertools": { "hashes": [ "sha256:5dd8bcf33e5f9513ffa06d5ad33d78f31e1931ac9a18f33d37e77a180d393a7c", @@ -213,12 +365,62 @@ ], "version": "==8.2.0" }, + "nbconvert": { + "hashes": [ + "sha256:21fb48e700b43e82ba0e3142421a659d7739b65568cc832a13976a77be16b523", + "sha256:f0d6ec03875f96df45aa13e21fd9b8450c42d7e1830418cccc008c0df725fcee" + ], + "version": "==5.6.1" + }, + "nbformat": { + "hashes": [ + "sha256:049af048ed76b95c3c44043620c17e56bc001329e07f83fec4f177f0e3d7b757", + "sha256:276343c78a9660ab2a63c28cc33da5f7c58c092b3f3a40b6017ae2ce6689320d" + ], + "version": "==5.0.6" + }, + "notebook": { + "hashes": [ + "sha256:3edc616c684214292994a3af05eaea4cc043f6b4247d830f3a2f209fa7639a80", + "sha256:47a9092975c9e7965ada00b9a20f0cf637d001db60d241d479f53c0be117ad48" + ], + "index": "pypi", + "version": "==6.0.3" + }, "packaging": { "hashes": [ - "sha256:170748228214b70b672c581a3dd610ee51f733018650740e98c7df862a583f73", - "sha256:e665345f9eef0c621aa0bf2f8d78cf6d21904eef16a93f020240b704a57f1334" + "sha256:3c292b474fda1671ec57d46d739d072bfd495a4f51ad01a055121d81e952b7a3", + "sha256:82f77b9bee21c1bafbf35a84905d604d5d1223801d639cf3ed140bd651c08752" + ], + "version": "==20.3" + }, + "pandocfilters": { + "hashes": [ + "sha256:b3dd70e169bb5449e6bc6ff96aea89c5eea8c5f6ab5e207fc2f521a2cf4a0da9" + ], + "version": "==1.4.2" + }, + "parso": { + "hashes": [ + "sha256:158c140fc04112dc45bca311633ae5033c2c2a7b732fa33d0955bad8152a8dd0", + "sha256:908e9fae2144a076d72ae4e25539143d40b8e3eafbaeae03c1bfe226f4cdf12c" + ], + "version": "==0.7.0" + }, + "pexpect": { + "hashes": [ + "sha256:0b48a55dcb3c05f3329815901ea4fc1537514d6ba867a152b581d69ae3710937", + "sha256:fc65a43959d153d0114afe13997d439c22823a27cefceb5ff35c2178c6784c0c" + ], + "markers": "sys_platform != 'win32'", + "version": "==4.8.0" + }, + "pickleshare": { + "hashes": [ + "sha256:87683d47965c1da65cdacaf31c8441d12b8044cdec9aca500cd78fc2c683afca", + "sha256:9649af414d74d4df115d5d718f82acb59c9d418196b7b4290ed47a12ce62df56" ], - "version": "==20.1" + "version": "==0.7.5" }, "pluggy": { "hashes": [ @@ -227,6 +429,27 @@ ], "version": "==0.13.1" }, + "prometheus-client": { + "hashes": [ + "sha256:71cd24a2b3eb335cb800c7159f423df1bd4dcd5171b234be15e3f31ec9f622da" + ], + "version": "==0.7.1" + }, + "prompt-toolkit": { + "hashes": [ + "sha256:563d1a4140b63ff9dd587bda9557cffb2fe73650205ab6f4383092fb882e7dc8", + "sha256:df7e9e63aea609b1da3a65641ceaf5bc7d05e0a04de5bd45d05dbeffbabf9e04" + ], + "version": "==3.0.5" + }, + "ptyprocess": { + "hashes": [ + "sha256:923f299cc5ad920c68f2bc0bc98b75b9f838b93b599941a6b63ddbc2476394c0", + "sha256:d7cc528d76e76342423ca640335bd3633420dc1366f258cb31d05e865ef5ca1f" + ], + "markers": "os_name != 'nt'", + "version": "==0.6.0" + }, "py": { "hashes": [ "sha256:5e27081401262157467ad6e7f851b7aa402c5852dbcb3dae06768434de5752aa", @@ -248,20 +471,33 @@ ], "version": "==2.1.1" }, + "pygments": { + "hashes": [ + "sha256:647344a061c249a3b74e230c739f434d7ea4d8b1d5f3721bc0f3558049b38f44", + "sha256:ff7a40b4860b727ab48fad6360eb351cc1b33cbf9b15a0f689ca5353e9463324" + ], + "version": "==2.6.1" + }, "pyparsing": { "hashes": [ - "sha256:4c830582a84fb022400b85429791bc551f1f4871c33f23e44f353119e92f969f", - "sha256:c342dccb5250c08d45fd6f8b4a559613ca603b57498511740e65cd11a2e7dcec" + "sha256:c203ec8783bf771a155b207279b9bccb8dea02d8f0c9e5f8ead507bc3246ecc1", + "sha256:ef9d7589ef3c200abe66653d3f1ab1033c3c419ae9b9bdb1240a85b024efc88b" + ], + "version": "==2.4.7" + }, + "pyrsistent": { + "hashes": [ + "sha256:28669905fe725965daa16184933676547c5bb40a5153055a8dee2a4bd7933ad3" ], - "version": "==2.4.6" + "version": "==0.16.0" }, "pytest": { "hashes": [ - "sha256:0d5fe9189a148acc3c3eb2ac8e1ac0742cb7618c084f3d228baaec0c254b318d", - "sha256:ff615c761e25eb25df19edddc0b970302d2a9091fbce0e7213298d85fb61fef6" + "sha256:0e5b30f5cb04e887b91b1ee519fa3d89049595f428c1db76e73bd7f17b09b172", + "sha256:84dde37075b8805f3d1f392cc47e38a0e59518fb46a431cfdaf7cf1ce805f970" ], "index": "pypi", - "version": "==5.3.5" + "version": "==5.4.1" }, "pytest-cov": { "hashes": [ @@ -271,12 +507,73 @@ "index": "pypi", "version": "==2.8.1" }, + "python-dateutil": { + "hashes": [ + "sha256:73ebfe9dbf22e832286dafa60473e4cd239f8592f699aa5adaf10050e6e1823c", + "sha256:75bb3f31ea686f1197762692a9ee6a7550b59fc6ca3a1f4b5d7e32fb98e2da2a" + ], + "version": "==2.8.1" + }, + "pyzmq": { + "hashes": [ + "sha256:0bbc1728fe4314b4ca46249c33873a390559edac7c217ec7001b5e0c34a8fb7f", + "sha256:1e076ad5bd3638a18c376544d32e0af986ca10d43d4ce5a5d889a8649f0d0a3d", + "sha256:242d949eb6b10197cda1d1cec377deab1d5324983d77e0d0bf9dc5eb6d71a6b4", + "sha256:26f4ae420977d2a8792d7c2d7bda43128b037b5eeb21c81951a94054ad8b8843", + "sha256:32234c21c5e0a767c754181c8112092b3ddd2e2a36c3f76fc231ced817aeee47", + "sha256:3f12ce1e9cc9c31497bd82b207e8e86ccda9eebd8c9f95053aae46d15ccd2196", + "sha256:4557d5e036e6d85715b4b9fdb482081398da1d43dc580d03db642b91605b409f", + "sha256:4f562dab21c03c7aa061f63b147a595dbe1006bf4f03213272fc9f7d5baec791", + "sha256:5e071b834051e9ecb224915398f474bfad802c2fff883f118ff5363ca4ae3edf", + "sha256:5e1f65e576ab07aed83f444e201d86deb01cd27dcf3f37c727bc8729246a60a8", + "sha256:5f10a31f288bf055be76c57710807a8f0efdb2b82be6c2a2b8f9a61f33a40cea", + "sha256:6aaaf90b420dc40d9a0e1996b82c6a0ff91d9680bebe2135e67c9e6d197c0a53", + "sha256:75238d3c16cab96947705d5709187a49ebb844f54354cdf0814d195dd4c045de", + "sha256:7f7e7b24b1d392bb5947ba91c981e7d1a43293113642e0d8870706c8e70cdc71", + "sha256:84b91153102c4bcf5d0f57d1a66a0f03c31e9e6525a5f656f52fc615a675c748", + "sha256:944f6bb5c63140d76494467444fd92bebd8674236837480a3c75b01fe17df1ab", + "sha256:a1f957c20c9f51d43903881399b078cddcf710d34a2950e88bce4e494dcaa4d1", + "sha256:a49fd42a29c1cc1aa9f461c5f2f5e0303adba7c945138b35ee7f4ab675b9f754", + "sha256:a99ae601b4f6917985e9bb071549e30b6f93c72f5060853e197bdc4b7d357e5f", + "sha256:ad48865a29efa8a0cecf266432ea7bc34e319954e55cf104be0319c177e6c8f5", + "sha256:b08e425cf93b4e018ab21dc8fdbc25d7d0502a23cc4fea2380010cf8cf11e462", + "sha256:bb10361293d96aa92be6261fa4d15476bca56203b3a11c62c61bd14df0ef89ba", + "sha256:bd1a769d65257a7a12e2613070ca8155ee348aa9183f2aadf1c8b8552a5510f5", + "sha256:cb3b7156ef6b1a119e68fbe3a54e0a0c40ecacc6b7838d57dd708c90b62a06dc", + "sha256:e8e4efb52ec2df8d046395ca4c84ae0056cf507b2f713ec803c65a8102d010de", + "sha256:f37c29da2a5b0c5e31e6f8aab885625ea76c807082f70b2d334d3fd573c3100a", + "sha256:f4d558bc5668d2345773a9ff8c39e2462dafcb1f6772a2e582fbced389ce527f", + "sha256:f5b6d015587a1d6f582ba03b226a9ddb1dfb09878b3be04ef48b01b7d4eb6b2a" + ], + "version": "==19.0.0" + }, + "qtconsole": { + "hashes": [ + "sha256:8f5ae5571f0e921db9f2d12613ed667c350ee22c7db598d9bbbe143e8533f932", + "sha256:e7882df6e95ec710b5893ec3a7ebfd54e410e63d801e4bbf8c785d74758c2329" + ], + "version": "==4.7.3" + }, + "qtpy": { + "hashes": [ + "sha256:2db72c44b55d0fe1407be8fba35c838ad0d6d3bb81f23007886dc1fc0f459c8d", + "sha256:fa0b8363b363e89b2a6f49eddc162a04c0699ae95e109a6be3bb145a913190ea" + ], + "version": "==1.9.0" + }, "requests": { "hashes": [ - "sha256:11e007a8a2aa0323f5a921e9e6a2d7e4e67d9877e85773fba9ba6419025cbeb4", - "sha256:9cf5292fcd0f598c671cfc1e0d7d1a7f13bb8085e9a590f48c010551dc6c4b31" + "sha256:43999036bfa82904b6af1d99e4882b560e5e2c68e5c4b0aa03b655f3d7d73fee", + "sha256:b3f43d496c6daba4493e7c431722aeb7dbc6288f52a6e04e7b6023b0247817e6" + ], + "version": "==2.23.0" + }, + "send2trash": { + "hashes": [ + "sha256:60001cc07d707fe247c94f74ca6ac0d3255aabcb930529690897ca2a39db28b2", + "sha256:f1691922577b6fa12821234aeb57599d887c4900b9ca537948d2dac34aea888b" ], - "version": "==2.22.0" + "version": "==1.5.0" }, "six": { "hashes": [ @@ -285,19 +582,68 @@ ], "version": "==1.14.0" }, + "terminado": { + "hashes": [ + "sha256:4804a774f802306a7d9af7322193c5390f1da0abb429e082a10ef1d46e6fb2c2", + "sha256:a43dcb3e353bc680dd0783b1d9c3fc28d529f190bc54ba9a229f72fe6e7a54d7" + ], + "version": "==0.8.3" + }, + "testpath": { + "hashes": [ + "sha256:60e0a3261c149755f4399a1fff7d37523179a70fdc3abdf78de9fc2604aeec7e", + "sha256:bfcf9411ef4bf3db7579063e0546938b1edda3d69f4e1fb8756991f5951f85d4" + ], + "version": "==0.4.4" + }, + "tornado": { + "hashes": [ + "sha256:0fe2d45ba43b00a41cd73f8be321a44936dc1aba233dee979f17a042b83eb6dc", + "sha256:22aed82c2ea340c3771e3babc5ef220272f6fd06b5108a53b4976d0d722bcd52", + "sha256:2c027eb2a393d964b22b5c154d1a23a5f8727db6fda837118a776b29e2b8ebc6", + "sha256:5217e601700f24e966ddab689f90b7ea4bd91ff3357c3600fa1045e26d68e55d", + "sha256:5618f72e947533832cbc3dec54e1dffc1747a5cb17d1fd91577ed14fa0dc081b", + "sha256:5f6a07e62e799be5d2330e68d808c8ac41d4a259b9cea61da4101b83cb5dc673", + "sha256:c58d56003daf1b616336781b26d184023ea4af13ae143d9dda65e31e534940b9", + "sha256:c952975c8ba74f546ae6de2e226ab3cc3cc11ae47baf607459a6728585bb542a", + "sha256:c98232a3ac391f5faea6821b53db8db461157baa788f5d6222a193e9456e1740" + ], + "version": "==6.0.4" + }, + "traitlets": { + "hashes": [ + "sha256:70b4c6a1d9019d7b4f6846832288f86998aa3b9207c6821f3578a6a6a467fe44", + "sha256:d023ee369ddd2763310e4c3eae1ff649689440d4ae59d7485eb4cfbbe3e359f7" + ], + "version": "==4.3.3" + }, "urllib3": { "hashes": [ - "sha256:2f3db8b19923a873b3e5256dc9c2dedfa883e33d87c690d9c7913e1f40673cdc", - "sha256:87716c2d2a7121198ebcb7ce7cccf6ce5e9ba539041cfbaeecfb641dc0bf6acc" + "sha256:3018294ebefce6572a474f0604c2021e33b3fd8006ecd11d62107a5d2a963527", + "sha256:88206b0eb87e6d677d424843ac5209e3fb9d0190d0ee169599165ec25e9d9115" ], - "version": "==1.25.8" + "version": "==1.25.9" }, "wcwidth": { "hashes": [ - "sha256:8fd29383f539be45b20bd4df0dc29c20ba48654a41e661925e612311e9f3c603", - "sha256:f28b3e8a6483e5d49e7f8949ac1a78314e740333ae305b4ba5defd3e74fb37a8" + "sha256:cafe2186b3c009a04067022ce1dcd79cb38d8d65ee4f4791b8888d6599d1bbe1", + "sha256:ee73862862a156bf77ff92b09034fc4825dd3af9cf81bc5b360668d425f3c5f1" + ], + "version": "==0.1.9" + }, + "webencodings": { + "hashes": [ + "sha256:a0af1213f3c2226497a97e2b3aa01a7e4bee4f403f95be16fc9acd2947514a78", + "sha256:b36a1c245f2d304965eb4e0a82848379241dc04b865afcc4aab16748587e1923" + ], + "version": "==0.5.1" + }, + "widgetsnbextension": { + "hashes": [ + "sha256:079f87d87270bce047512400efd70238820751a11d2d8cb137a5a5bdbaf255c7", + "sha256:bd314f8ceb488571a5ffea6cc5b9fc6cba0adaf88a9d2386b93a489751938bcd" ], - "version": "==0.1.8" + "version": "==3.5.1" } } } diff --git a/temp.txt b/temp.txt new file mode 100644 index 0000000..52b9eec --- /dev/null +++ b/temp.txt @@ -0,0 +1,30 @@ +1: lo: mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000 + link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 + inet 127.0.0.1/8 scope host lo + valid_lft forever preferred_lft forever + inet6 ::1/128 scope host + valid_lft forever preferred_lft forever +2: enp0s25: mtu 1500 qdisc fq_codel state UP group default qlen 1000 + link/ether 3c:97:0e:8b:26:76 brd ff:ff:ff:ff:ff:ff + inet 192.168.25.173/24 brd 192.168.25.255 scope global dynamic noprefixroute enp0s25 + valid_lft 76070sec preferred_lft 76070sec + inet6 2804:7f3:8382:71b5:c848:fb2b:5d9d:e25/64 scope global temporary dynamic + valid_lft 76031sec preferred_lft 4031sec + inet6 2804:7f3:8382:71b5:34c:c572:e340:b672/64 scope global dynamic mngtmpaddr noprefixroute + valid_lft 76031sec preferred_lft 4031sec + inet6 fe80::fbfb:4866:cdb0:8825/64 scope link noprefixroute + valid_lft forever preferred_lft forever +3: wlp3s0: mtu 1500 qdisc mq state UP group default qlen 1000 + link/ether 6c:88:14:3b:60:a8 brd ff:ff:ff:ff:ff:ff + inet 192.168.25.234/24 brd 192.168.25.255 scope global dynamic noprefixroute wlp3s0 + valid_lft 76070sec preferred_lft 76070sec + inet6 2804:7f3:8382:71b5:7138:b144:c2e:e7cf/64 scope global temporary dynamic + valid_lft 76031sec preferred_lft 4031sec + inet6 2804:7f3:8382:71b5:c6a7:1535:8a2c:106e/64 scope global dynamic mngtmpaddr noprefixroute + valid_lft 76031sec preferred_lft 4031sec + inet6 fe80::53cf:475a:47c2:4ee8/64 scope link noprefixroute + valid_lft forever preferred_lft forever +4: docker0: mtu 1500 qdisc noqueue state DOWN group default + link/ether 02:42:68:de:6f:83 brd ff:ff:ff:ff:ff:ff + inet 172.17.0.1/16 brd 172.17.255.255 scope global docker0 + valid_lft forever preferred_lft forever diff --git a/tests/simulator/__init__.py b/tests/simulator/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/simulator/test_initial_connection.py b/tests/simulator/test_initial_connection.py new file mode 100644 index 0000000..b40376c --- /dev/null +++ b/tests/simulator/test_initial_connection.py @@ -0,0 +1,43 @@ +import pytest +import simpy + +from simulator.network import Network +from simulator.processor import Processor +from simulator.driver import Driver +from simulator.peer import Peer + + +def test_connection(): + # create env + env = simpy.Environment() + # network + net = Network(env,2) + #create peers + teste = env.timeout(50) + + proc = Processor(env, 0, 3) + dri = Driver(net, proc) + new_peer = Peer(dri, 0) + env.process(dri.connect()) + + env.run(until=10) + + assert dri.address != None + +def test_timeout_keep_alive(): + # create env + env = simpy.Environment() + # network + net = Network(env,2) + #create peers + teste = env.timeout(50) + + proc = Processor(env, 0, 3) + dri = Driver(net, proc) + new_peer = Peer(dri, 0) + + env.process(dri.disconnect()) + + env.run(until=10) + + assert dri.address == None From 0afb2be9e012820ac4f12990070e9f360dd4863e Mon Sep 17 00:00:00 2001 From: asbmails2 Date: Tue, 5 May 2020 02:12:18 -0300 Subject: [PATCH 02/12] rm temp.txt - It was created by mistake --- temp.txt | 30 ------------------------------ 1 file changed, 30 deletions(-) delete mode 100644 temp.txt diff --git a/temp.txt b/temp.txt deleted file mode 100644 index 52b9eec..0000000 --- a/temp.txt +++ /dev/null @@ -1,30 +0,0 @@ -1: lo: mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000 - link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 - inet 127.0.0.1/8 scope host lo - valid_lft forever preferred_lft forever - inet6 ::1/128 scope host - valid_lft forever preferred_lft forever -2: enp0s25: mtu 1500 qdisc fq_codel state UP group default qlen 1000 - link/ether 3c:97:0e:8b:26:76 brd ff:ff:ff:ff:ff:ff - inet 192.168.25.173/24 brd 192.168.25.255 scope global dynamic noprefixroute enp0s25 - valid_lft 76070sec preferred_lft 76070sec - inet6 2804:7f3:8382:71b5:c848:fb2b:5d9d:e25/64 scope global temporary dynamic - valid_lft 76031sec preferred_lft 4031sec - inet6 2804:7f3:8382:71b5:34c:c572:e340:b672/64 scope global dynamic mngtmpaddr noprefixroute - valid_lft 76031sec preferred_lft 4031sec - inet6 fe80::fbfb:4866:cdb0:8825/64 scope link noprefixroute - valid_lft forever preferred_lft forever -3: wlp3s0: mtu 1500 qdisc mq state UP group default qlen 1000 - link/ether 6c:88:14:3b:60:a8 brd ff:ff:ff:ff:ff:ff - inet 192.168.25.234/24 brd 192.168.25.255 scope global dynamic noprefixroute wlp3s0 - valid_lft 76070sec preferred_lft 76070sec - inet6 2804:7f3:8382:71b5:7138:b144:c2e:e7cf/64 scope global temporary dynamic - valid_lft 76031sec preferred_lft 4031sec - inet6 2804:7f3:8382:71b5:c6a7:1535:8a2c:106e/64 scope global dynamic mngtmpaddr noprefixroute - valid_lft 76031sec preferred_lft 4031sec - inet6 fe80::53cf:475a:47c2:4ee8/64 scope link noprefixroute - valid_lft forever preferred_lft forever -4: docker0: mtu 1500 qdisc noqueue state DOWN group default - link/ether 02:42:68:de:6f:83 brd ff:ff:ff:ff:ff:ff - inet 172.17.0.1/16 brd 172.17.255.255 scope global docker0 - valid_lft forever preferred_lft forever From 7eaa1984755635147a174d9cf636f3dc263db421 Mon Sep 17 00:00:00 2001 From: MA Date: Mon, 18 May 2020 19:51:18 -0300 Subject: [PATCH 03/12] Initial DDS code --- simulator/__init__.py | 0 simulator/custom_error.py | 8 +++ simulator/driver.py | 55 +++++++++------ simulator/mockup.py | 51 ++++++++++++++ simulator/network.py | 57 +++++++++------- simulator/peer.py | 11 +-- simulator/run.py | 5 +- simulator/simple_dds.py | 139 ++++++++++++++++++++++++++++++++++++++ simulator/singleton.py | 13 ++++ 9 files changed, 283 insertions(+), 56 deletions(-) create mode 100644 simulator/__init__.py create mode 100644 simulator/custom_error.py create mode 100644 simulator/mockup.py create mode 100644 simulator/simple_dds.py create mode 100644 simulator/singleton.py diff --git a/simulator/__init__.py b/simulator/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/simulator/custom_error.py b/simulator/custom_error.py new file mode 100644 index 0000000..38c6687 --- /dev/null +++ b/simulator/custom_error.py @@ -0,0 +1,8 @@ + +class Error(Exception): + pass + +class RegistrationError(Error): + + def __init__(self, message): + self.message = message \ No newline at end of file diff --git a/simulator/driver.py b/simulator/driver.py index 890b56c..552c10b 100644 --- a/simulator/driver.py +++ b/simulator/driver.py @@ -3,6 +3,8 @@ import simpy import logging +import custom_error + class Driver: def __init__(self, network, processor): @@ -18,33 +20,47 @@ def __init__(self, network, processor): 'on_disconnect': [] } self.keep_alive_interval = 20 + self.peer_list = [] + self.message_buffer = [] def run(self): - if self.address is None: - for z in self.connect(): - yield z - self.env.process(self.send_keepalive()) # A partir de agora, mandamos um sinal de vida constantemente - - while True: - event = yield self.async_events.get() - for z in self.issue_event(event[0], event[1]): - if z: - yield z - for z in self.issue_event(event[0], 'on_advertise'): - if z: - yield z + for z in self.connect(): + yield z + self.fetch_peer_list() + + # while True: + # event = yield self.async_events.get() + # for z in self.issue_event(event[0], event[1]): + # if z: + # yield z + # for z in self.issue_event(event[0], 'on_advertise'): + # if z: + # yield z def connect(self): - for z in self.network.register(self): - yield z - for z in self.issue_event('on_connect', self.address): - yield z + while True: # Tenta conectar-se repetidamente + try: + for z in self.network.register(self): + yield z + for z in self.issue_event('on_connect', self.address): + yield z + break + except RegistrationError as err: + print(err.message) + yield self.env.timeout(1) + + def fetch_peer_list(self): + self.peer_list = self.network.send_addresses(self) + + def disconnect(self): + former_address = self.address + self.address = None def advertise(self, msg): - msg = 'ADV-'+str(msg) + msg = '(ADV) '+str(msg) return self.network.send_broadcast(self.address, msg) - def recieve (self, msg_envelope): + def receive (self, msg_envelope): logging.info(str(self.env.now) + ' :: ' + '{} received from {}: {}'.format( msg_envelope[1], msg_envelope[0], msg_envelope[2])) @@ -65,5 +81,6 @@ def issue_event (self, event, value=None): yield z def send_keepalive(self): + while True: yield self.env.timeout(self.keep_alive_interval) self.network.renew(self.address) diff --git a/simulator/mockup.py b/simulator/mockup.py new file mode 100644 index 0000000..58c7f7e --- /dev/null +++ b/simulator/mockup.py @@ -0,0 +1,51 @@ +import simpy +import logging + +import customdds + +# Neste exemplo, o único tipo de dado que será usado é string, mas a especificação DDS permite a definição de tipos personalizados. +# De fato, a definição e registro de um tipo de dado são ações necessárias numa implementação real. +# Aqui, tentei escrever como um cliente faria uso desse API. Ainda é necessário implementar de fato. Mesmo assim, fica claro que... +# ... o uso do API simplifica bastante o trabalho do usuário, que não precisa se preocupar com o funcionamento da rede e detalhes de transmissão. +# Só pra enfatizar, isto é uma simplificação EXTREMA do DDS, mais precisamente, da implementação OpenDDS. Eu tentei capturar o que eu compreendi sobre ele. + +class ApplicationMockup: + + def __init__(self, domainID): + domainParticipantFactory = customdds.DomainParticipantFactoryImpl.get_instance() # Singleton + # Criamos participante escolhendo uma ID para o domínio + self.participant = domainParticipantFactory.create_participant(domainID) + + def run(self): + # Criamos um tópico + topic_name = "Test" + topic = self.participant.create_topic(topic_name) + # Criamos um publisher + # Dentro de um publisher, é necessário haver uma referência ao DomainParticipant que o criou + publisher = self.participant.create_publisher() + # Criamos um DataWriter com o tópico + writer = publisher.create_datawriter(topic) + + # Podemos esperar que haja pelo menos um subscriber antes de enviar a mensagem.. + # + # Prosseguindo para a escrita + + message_content = "This is a test message" + # Digamos que previamente tenhamos criado um tipo 'Message', de forma que possamos encapsular o conteúdo da mensagem em si... + # ... assim como outras metainformações. + # Dados existem em uma instância, a qual é uma entidade, tendo, assim, um identificador único no domínio. + msg_id = 63 + new_msg = (msg_id, message_content) + # O resultado do método a seguir deve ser o envio da mensagem para todos os subscribers do tópico + writer.write(new_msg) + + # Para o estabelecimento de um subscriber, o código seria bem parecido, exceto que usaríamos um DataReader para ler as mensagens com métodos 'read()' e 'take()'. + + + +# Algumas notas adicionais: +# - No OpenDDS, pelo que entendi, existe um singleton denominado 'TheServiceParticipant' responsável pelo controle das entidades, etc. Esse 'Service' permite que... +# ... haja acesso aos domínios, tópicos, participantes, publishers, subscribers, instâncias de dados, ... ou seja, todas as entidades. +# - O uso de políticas QoS servem para configurar o API, de certa forma impondo exigências no funcionamento do serviço. Uma lista dessas políticas está presente na especificação. +# ... Decidi omitir o uso de QoS policies para simplificar, mas, numa implementação real, elas são indispensáveis. +# - Além do DDS, parece-me ser necessário mais uma camada de abstração. No OpenDDS, eles usam um API chamado TAO. \ No newline at end of file diff --git a/simulator/network.py b/simulator/network.py index 0ef0c12..f0d8524 100644 --- a/simulator/network.py +++ b/simulator/network.py @@ -2,6 +2,8 @@ import simpy import logging +import custom_error + class Network: def __init__(self, env, latency, max_hosts = 100): @@ -17,13 +19,13 @@ def __init__(self, env, latency, max_hosts = 100): self.default_lease_time = 40 self.addr_list = [{'node':None, 'lease':0} for i in range(self.max_hosts)] # O índice da lista serve como 'IP' self.node_list = [] # Usamos esta lista para fazer broadcasts e checar empréstimos - self.env.process(self.dhcp()) def register(self, node_driver): with self.channel.request() as rec: yield rec if self.full_capacity: logging.warning(str(self.env.now) + ' :: ' + 'Could not register node: Network at full capacity') + raise RegistrationError("Network at full capacity") else: curr_address = self.next_available_address logging.info(str(self.env.now) + ' :: ' + 'connecting {}'.format(curr_address)) @@ -39,42 +41,42 @@ def register(self, node_driver): self.find_next_available() yield self.timeout(self.latency) + def confirm_peer(self, address): + if address < 0 or address >= self.max_hosts: + raise RuntimeError("Invalid address") + elif self.addr_list[address]['node'] is None: + raise RuntimeError("Address not registered") + # Substituir por um erro personalizado depois? + # Talvez expandir verificação para que veja se o endereço realmente corresponde.. + # .. ao driver que enviou mensagem + # Talvez adicionar mensagem de debug? + + # Adicionar try.. except nas funções de unicast e broadcast, para lidar com erros def send_unicast(self, from_addr, to_addr, msg): + self.confirm_peer(from_addr) + self.confirm_peer(to_addr) logging.info(str(self.env.now) + ' :: ' + 'network sending unicast {} => {}'.format(from_addr, to_addr)) - if(to_addr <= 0): - print('{} address not found (msg from {})'.format( - to_addr, from_addr)) - yield self.env.timeout(0) - else: + with self.channel.request() as rec: + yield rec msg_envelope = [from_addr, to_addr, msg] - with self.channel.request() as rec: - yield rec - node = self.addr_list[to_addr]['node'] - if node: - node.recieve(msg_envelope) - yield self.env.timeout(self.latency) - print(msg_envelope) - else: - print('{} address not found (msg from {})'.format( - to_addr, from_addr)) + node = self.addr_list[to_addr]['node'] + node.receive(msg_envelope) + yield self.env.timeout(self.latency) def send_broadcast(self, from_addr, msg): + self.confirm_peer(from_addr) logging.info(str(self.env.now) + ' :: ' + 'Message Broadcast from {} - {}'.format(from_addr,msg)) with self.node_list_access.request(priority=0) as nl_access: yield nl_access for addr in range(len(self.node_list)): to_addr = self.node_list[addr]['address'] - msg_envelope2 = [from_addr, to_addr, msg] + msg_envelope = [from_addr, to_addr, msg] with self.channel.request() as rec: yield rec node = self.node_list[addr]['node'] - if node: # Checando se o nodo ainda faz parte da rede - node.recieve(msg_envelope2) - yield self.env.timeout(self.latency) - logging.info(str(self.env.now) + ' :: ' + 'Broadcast:'+ str(msg_envelope2)) - else: - logging.info(str(self.env.now) + ' :: ' + '{} address not found (msg from {})'.format( - to_addr, from_addr)) + node.receive(msg_envelope) + yield self.env.timeout(self.latency) + logging.info(str(self.env.now) + ' :: ' + 'Broadcast:'+ str(msg_envelope)) def find_next_available(self): addr = (self.next_available_address + 1) % self.max_hosts @@ -112,6 +114,13 @@ def end_lease(self, index): self.full_capacity = False logging.info(str(self.env.now) + ' :: ' + 'Lease ended for address {}'.format(address)) + def send_addresses(self, driver): + addr_list = [] + for peer in self.node_list: + if peer['address'] is not driver.address: + addr_list.append(peer['address']) + return addr_list + def dhcp(self): while True: for z in self.check_lease(): diff --git a/simulator/peer.py b/simulator/peer.py index 92484f8..4b1b5e7 100644 --- a/simulator/peer.py +++ b/simulator/peer.py @@ -38,13 +38,4 @@ def on_disconnect (self): def on_advertise (self, msg): for z in self.driver.advertise(msg): - yield z - - - - - - - - - + yield z \ No newline at end of file diff --git a/simulator/run.py b/simulator/run.py index ea5cb31..bdafd4b 100644 --- a/simulator/run.py +++ b/simulator/run.py @@ -19,9 +19,8 @@ handlers = handlers ) -NUM_PEERS = 2 -SIM_DURATION = 1000 - +NUM_PEERS = 1 +SIM_DURATION = 300 # create env env = simpy.Environment() diff --git a/simulator/simple_dds.py b/simulator/simple_dds.py new file mode 100644 index 0000000..3119bc9 --- /dev/null +++ b/simulator/simple_dds.py @@ -0,0 +1,139 @@ +import logging + +import singleton + +# Possíveis problemas de uso mútuo nesta classe? Veremos quando testarmos. +class UniqueHandleController(metaclass=Singleton): + + def __init__(self): + self.next_available_handle = 0 + + def generate_handle(self) + handle = self.next_available_handle + self.next_available_handle += 1 + return handle + +class Entity: + + def __init__(self): + self.instance_handle = 0 + + def set_instance_handle(self, handle) + if self.instance_handle is not 0: + raise RuntimeError("DDS Instance already has a handle.") + else + self.instance_handle = handle + + def get_instance_handle(self): + if self.instance_handle is 0: + raise RuntimeError("DDS Instance has not been assigned a handle") + return self.instance_handle + +class DDS_Service(Entity): + + def __init__(self, driver, handle_controller): + self.handle_controller = handle_controller + self.instance_handle = self.handle_controller.generate_handle() + self.driver = driver + self.handles = {} + self.participants = {} + self.topics = {} + self.data_objects = {} + + def set_instance_handle(self, handle): + raise RuntimeError("DDS Service's handle cannot be changed.") + + def get_instance_handle(self): + return self.instance_handle + + def assign_handle(self, entity): + handle = self.handle_controller.generate_handle() + entity.set_instance_handle(handle) + self.handles[handle] = entity + self.next_available_instance += 1 + + def add_participant(self, participant): + self.assign_handle(participant) + handle = participant.get_instance_handle() + self.participants[handle] = participant + + def add_topic(self, topic): + self.assign_handle(topic) + topic_key = topic.get_name() + self.topics[topic_key] = topic + + def topic_exists(self, topic_name): + return topic_name in self.topics + + def run(self): + # Através do driver, queremos: + # - Achar participantes + # - Atualizar instâncias de dados + # - Enviar dados relevantes aos subscribers + pass + +class Domain_Participant(Entity): + + def __init__(self, dds_service): + self.service = dds_service + self.publishers = {} + self.subscribers = {} + self.topics = {} + self.discovered_participants = {} + + self.dds_service.add_participant(self) + + def create_topic(self, topic_name): + if self.service.topic_exists(topic_name): + logging.warning(f'{topic_name} already exists.') + else + new_topic = Topic(topic_name, self) + self.service.add_topic(new_topic) + self.topics.append(new_topic) + + def delete_topic(self, topic): + # Pré-condição: tópico deve ter sido criado por este participante + pass + + def find_topic(self, topic_name): + # Deve retornar um objeto do tipo Topic + pass + + def create_publisher(self, topic): + new_publisher = Publisher(self, topic) + self.service.assign_handle(new_publisher) + handle = new_publisher.get_instance_handle() + self.publishers[handle] = new_publisher + + def delete_publisher(self, publisher): + pass + + def create_subscriber(self, topic): + new_subscriber = Subscriber(self, topic) + self.service.assign_handle(new_subscriber) + handle = new_subscriber.get_instance_handle() + self.subscribers[handle] = new_subscriber + + def delete_subscriber(self, subscriber): + pass + +class Topic(Entity): + + def __init__(self, topic_name, participant): + self.name = topic_name + self.parent = participant + self.publishers = [] + self.subscribers = [] + +class Publisher(Entity): + + def __init__(self, participant, topic): + self.parent = participant + self.topic = topic + self.data_buffer = [] + + def write(self, message): + pass + +class Subscriber(Entity): + pass diff --git a/simulator/singleton.py b/simulator/singleton.py new file mode 100644 index 0000000..372d71c --- /dev/null +++ b/simulator/singleton.py @@ -0,0 +1,13 @@ +# Referências +# - https://stackoverflow.com/questions/6760685/creating-a-singleton-in-python +# - https://stackoverflow.com/questions/100003/what-are-metaclasses-in-python + +class Singleton(type): + _instances = {} + def __call__(cls, *args, **kwargs): + if cls not in cls._instances: + cls._instances[cls] = super(Singleton, cls).__call__(*args, **kwargs) + return cls._instances[cls] + +class ExampleSingleton(metaclass=Singleton): + pass \ No newline at end of file From ea66fe20aeae69674f83c5f0609fc9ed1d5adc45 Mon Sep 17 00:00:00 2001 From: MA Date: Wed, 20 May 2020 19:12:27 -0300 Subject: [PATCH 04/12] Added more methods, attempted to correct interaction with driver. Code still not fit to execute. --- simulator/driver.py | 26 ++++----- simulator/network.py | 4 +- simulator/peer.py | 14 ++++- simulator/simple_dds.py | 124 ++++++++++++++++++++++++++++++++++++---- 4 files changed, 140 insertions(+), 28 deletions(-) diff --git a/simulator/driver.py b/simulator/driver.py index 552c10b..72d9e2c 100644 --- a/simulator/driver.py +++ b/simulator/driver.py @@ -20,22 +20,19 @@ def __init__(self, network, processor): 'on_disconnect': [] } self.keep_alive_interval = 20 - self.peer_list = [] - self.message_buffer = [] def run(self): for z in self.connect(): yield z - self.fetch_peer_list() - # while True: - # event = yield self.async_events.get() - # for z in self.issue_event(event[0], event[1]): - # if z: - # yield z - # for z in self.issue_event(event[0], 'on_advertise'): - # if z: - # yield z + while True: + event = yield self.async_events.get() + for z in self.issue_event(event[0], event[1]): + if z: + yield z + # for z in self.issue_event(event[0], 'on_advertise'): + # if z: + # yield z def connect(self): while True: # Tenta conectar-se repetidamente @@ -44,13 +41,13 @@ def connect(self): yield z for z in self.issue_event('on_connect', self.address): yield z - break + break # Se chegarmos aqui, código completado com sucesso, saímos do loop except RegistrationError as err: print(err.message) yield self.env.timeout(1) def fetch_peer_list(self): - self.peer_list = self.network.send_addresses(self) + return self.network.send_addresses(self) def disconnect(self): former_address = self.address @@ -84,3 +81,6 @@ def send_keepalive(self): while True: yield self.env.timeout(self.keep_alive_interval) self.network.renew(self.address) + + def get_time(self): + return self.env.now \ No newline at end of file diff --git a/simulator/network.py b/simulator/network.py index f0d8524..65b0b75 100644 --- a/simulator/network.py +++ b/simulator/network.py @@ -43,9 +43,9 @@ def register(self, node_driver): def confirm_peer(self, address): if address < 0 or address >= self.max_hosts: - raise RuntimeError("Invalid address") + raise RuntimeError(f"Invalid address: {address}") elif self.addr_list[address]['node'] is None: - raise RuntimeError("Address not registered") + raise RuntimeError(f"Address not registered: {address}") # Substituir por um erro personalizado depois? # Talvez expandir verificação para que veja se o endereço realmente corresponde.. # .. ao driver que enviou mensagem diff --git a/simulator/peer.py b/simulator/peer.py index 4b1b5e7..df979a3 100644 --- a/simulator/peer.py +++ b/simulator/peer.py @@ -1,11 +1,14 @@ import logging +import simple_dds + """ -Class peer for create all the base stack for peer +Simulates the behavior of a peer in a network. +Uses driver object to interface with the network. Definir as caracteristicas do nodo basicas - ID , Recursos - Qualidade -riar um canal broadcast onde o peer precisa se conectar +Criar um canal broadcast onde o peer precisa se conectar E criar os links de comunicacao unicast agente tem (ref de um canal broad) @@ -38,4 +41,9 @@ def on_disconnect (self): def on_advertise (self, msg): for z in self.driver.advertise(msg): - yield z \ No newline at end of file + yield z + + def dds_test (self): + handle_controller = UniqueHandleController() # Criamos o gerador de identificadores. Se já existe, pegamos só uma referência + the_service = DDS_Service(self.driver, handle_controller) # Iniciamos o serviço DDS + diff --git a/simulator/simple_dds.py b/simulator/simple_dds.py index 3119bc9..03c12c7 100644 --- a/simulator/simple_dds.py +++ b/simulator/simple_dds.py @@ -1,4 +1,5 @@ import logging +from threading import Lock import singleton @@ -6,12 +7,33 @@ class UniqueHandleController(metaclass=Singleton): def __init__(self): - self.next_available_handle = 0 + self.next_available_handle = 1 + self.lock = threading.Lock() def generate_handle(self) + handle = None + with self.lock: handle = self.next_available_handle self.next_available_handle += 1 - return handle + return handle + +# Classe criada para impedir conflito entre leitura e escrita. +# Poderia fazer a lógica usando um Resource do simpy, mas prefiro evitar o uso.. +# ... do simpy na lógica do DDS. +class Data_Buffer: + + def __init__(self): + self.data_buffer = [] + self.lock = threading.Lock() + + def write_to(self, data): + with self.lock: + self.data_buffer.append(data) + + def read_from(self, container): + with self.lock: + container = copy(self.data_buffer) + self.data_buffer = [] class Entity: @@ -29,16 +51,21 @@ def get_instance_handle(self): raise RuntimeError("DDS Instance has not been assigned a handle") return self.instance_handle +# Talvez seja necessário fazer dictionaries personalizados, que evitem problemas de acesso mútuo. class DDS_Service(Entity): def __init__(self, driver, handle_controller): self.handle_controller = handle_controller self.instance_handle = self.handle_controller.generate_handle() self.driver = driver - self.handles = {} - self.participants = {} - self.topics = {} - self.data_objects = {} + self.peer_list = [] + self.data_buffer = Data_Buffer() + self.local_changes = Data_Buffer() + self.handles = {} # Handle: Entity + self.participants = {} # Handle: Participant + self.topics = {} # Topic name: Topic + self.data_objects = {} # Handle: Data object + self.message_handlers = {} def set_instance_handle(self, handle): raise RuntimeError("DDS Service's handle cannot be changed.") @@ -46,31 +73,105 @@ def set_instance_handle(self, handle): def get_instance_handle(self): return self.instance_handle + # TODO: Notificar na rede quando houver atualização, para todos os métodos 'add' + + # Enfileiramos todas as mudanças locais, para mandá-las na rede de uma vez só. + def queue_local_modification(self, type_name, data): + change = (type_name, data) + self.local_changes.write_to(change) + + # Fazer verificação de handle duplicada def assign_handle(self, entity): handle = self.handle_controller.generate_handle() entity.set_instance_handle(handle) self.handles[handle] = entity - self.next_available_instance += 1 def add_participant(self, participant): self.assign_handle(participant) handle = participant.get_instance_handle() self.participants[handle] = participant + self.queue_local_modification(('NEW_PARTICIPANT', participant)) def add_topic(self, topic): self.assign_handle(topic) topic_key = topic.get_name() self.topics[topic_key] = topic + self.queue_local_modification(('NEW_TOPIC', topic)) def topic_exists(self, topic_name): return topic_name in self.topics + def get_topic(self, topic_name) + if self.topic_exists(topic_name): + return self.topics[topic_name] + else: # Como lidar com isto? + pass + + def discover_peers(self): + self.peer_list = self.driver.fetch_peer_list() + + def receive_incoming_data(self, msg): + self.data_buffer.write_to(msg) + logging.info(str(self.driver.get_time() + ' :: ' + f'Data received by DDS Service, handle {self.instance_handle}')) + + def append_remote_participant(self, r_participant): + handle = r_participant.get_instance_handle() + if handle not in self.participants and handle not in self.handles: + self.handles[handle] = r_participant + self.participants[handle] = r_participant + + def append_remote_topics(self, r_topic): + topic_name = r_topic.get_name() + if not self.topic_exists(topic_name): + handle = r_topic.get_instance_handle() + self.handles[handle] = r_topic + self.topics[topic_name] = r_topic + else: + self.resolve_topic_conflict(r_topic) + + def update_data_objects(self, r_data): + handle = r_data.get_instance_handle() + self.data_objects[handle] = r_data + if handle not in self.handles: + self.handles[handle] = r_data + + def attach_msg_reception_handler_to_driver(self): + self.driver.register_handler(self.receive_incoming_data, 'on_message') + + # Não gosto muito deste nome. + def interpret_data(self, data): + # Presumimos que os dados estejam em uma 2-tupla, sendo o primeiro elemento.. + # .. uma string descrevendo o pedido, o segundo elemento os dados em si + if data[1] not in self.message_handlers: + logging.warning(str(self.driver.get_time() + ' :: ' + f'DDS Service (Handle {self.instance_handle}): Invalid request: {data[1]}')) + else + self.message_handlers[data[1]](data[2]) + + def process_received_messages(self): + message_queue = [] + self.data_buffer.read_from(message_queue) + for message in message_queue: + self.interpret_data(message) + + def propagate_local_changes(self): + changes_to_send = [] + self.local_changes.write_to(changes_to_send) + message = ('UPDATE', changes_to_send) + # Aqui, usaríamos o método advertise do driver, mas ele só lida com msgs tipo string. E agora? + + def setup(self): + self.add_message_handler_methods() + self.attach_msg_reception_handler_to_driver() + self.discover_peers() + def run(self): # Através do driver, queremos: # - Achar participantes # - Atualizar instâncias de dados # - Enviar dados relevantes aos subscribers - pass + # - Processar dados recebidos + self.setup() + class Domain_Participant(Entity): @@ -79,8 +180,6 @@ def __init__(self, dds_service): self.publishers = {} self.subscribers = {} self.topics = {} - self.discovered_participants = {} - self.dds_service.add_participant(self) def create_topic(self, topic_name): @@ -117,6 +216,9 @@ def create_subscriber(self, topic): def delete_subscriber(self, subscriber): pass + def get_discovered_participants(self): + # Usar o service + class Topic(Entity): def __init__(self, topic_name, participant): @@ -125,6 +227,8 @@ def __init__(self, topic_name, participant): self.publishers = [] self.subscribers = [] + def get_name(self): + class Publisher(Entity): def __init__(self, participant, topic): From 14336b424abbc5ea1aaf8c73cbbeb91eef938348 Mon Sep 17 00:00:00 2001 From: MA Date: Wed, 20 May 2020 19:28:08 -0300 Subject: [PATCH 05/12] Fixed syntax errors. --- simulator/simple_dds.py | 44 ++++++++++++++++++++++++----------------- 1 file changed, 26 insertions(+), 18 deletions(-) diff --git a/simulator/simple_dds.py b/simulator/simple_dds.py index 03c12c7..c0f6f73 100644 --- a/simulator/simple_dds.py +++ b/simulator/simple_dds.py @@ -1,7 +1,7 @@ import logging from threading import Lock -import singleton +from singleton import Singleton # Possíveis problemas de uso mútuo nesta classe? Veremos quando testarmos. class UniqueHandleController(metaclass=Singleton): @@ -10,12 +10,12 @@ def __init__(self): self.next_available_handle = 1 self.lock = threading.Lock() - def generate_handle(self) - handle = None - with self.lock: - handle = self.next_available_handle - self.next_available_handle += 1 - return handle + def generate_handle(self): + handle = None + with self.lock: + handle = self.next_available_handle + self.next_available_handle += 1 + return handle # Classe criada para impedir conflito entre leitura e escrita. # Poderia fazer a lógica usando um Resource do simpy, mas prefiro evitar o uso.. @@ -35,19 +35,22 @@ def read_from(self, container): container = copy(self.data_buffer) self.data_buffer = [] + def is_empty(self): + return len(self.data_buffer) == 0 + class Entity: def __init__(self): self.instance_handle = 0 - def set_instance_handle(self, handle) - if self.instance_handle is not 0: + def set_instance_handle(self, handle): + if self.instance_handle != 0: raise RuntimeError("DDS Instance already has a handle.") - else + else: self.instance_handle = handle def get_instance_handle(self): - if self.instance_handle is 0: + if self.instance_handle == 0: raise RuntimeError("DDS Instance has not been assigned a handle") return self.instance_handle @@ -101,7 +104,7 @@ def add_topic(self, topic): def topic_exists(self, topic_name): return topic_name in self.topics - def get_topic(self, topic_name) + def get_topic(self, topic_name): if self.topic_exists(topic_name): return self.topics[topic_name] else: # Como lidar com isto? @@ -144,7 +147,7 @@ def interpret_data(self, data): # .. uma string descrevendo o pedido, o segundo elemento os dados em si if data[1] not in self.message_handlers: logging.warning(str(self.driver.get_time() + ' :: ' + f'DDS Service (Handle {self.instance_handle}): Invalid request: {data[1]}')) - else + else: self.message_handlers[data[1]](data[2]) def process_received_messages(self): @@ -154,10 +157,13 @@ def process_received_messages(self): self.interpret_data(message) def propagate_local_changes(self): - changes_to_send = [] - self.local_changes.write_to(changes_to_send) - message = ('UPDATE', changes_to_send) - # Aqui, usaríamos o método advertise do driver, mas ele só lida com msgs tipo string. E agora? + if not self.local_changes.is_empty(): + changes_to_send = [] + self.local_changes.write_to(changes_to_send) + message = ('UPDATE', changes_to_send) + # Aqui, usaríamos o método advertise do driver, mas ele só lida com msgs tipo string. E agora? + # Como lidar com casos de problemas de conexão e falha de envio? Talvez .. + # .. mantê-las e tentar de novo, mas como implementar isso no simulador? def setup(self): self.add_message_handler_methods() @@ -185,7 +191,7 @@ def __init__(self, dds_service): def create_topic(self, topic_name): if self.service.topic_exists(topic_name): logging.warning(f'{topic_name} already exists.') - else + else: new_topic = Topic(topic_name, self) self.service.add_topic(new_topic) self.topics.append(new_topic) @@ -217,6 +223,7 @@ def delete_subscriber(self, subscriber): pass def get_discovered_participants(self): + pass # Usar o service class Topic(Entity): @@ -228,6 +235,7 @@ def __init__(self, topic_name, participant): self.subscribers = [] def get_name(self): + pass class Publisher(Entity): From 5577b56dcf20515e3cb7ad351f5a00bf5eafcdb3 Mon Sep 17 00:00:00 2001 From: MA Date: Sun, 24 May 2020 14:52:17 -0300 Subject: [PATCH 06/12] Saving WIP --- simulator/driver.py | 6 +- simulator/peer.py | 1 - simulator/simple_dds.py | 148 ++++++++++++++++++++++++++-------------- 3 files changed, 100 insertions(+), 55 deletions(-) diff --git a/simulator/driver.py b/simulator/driver.py index 72d9e2c..29e08aa 100644 --- a/simulator/driver.py +++ b/simulator/driver.py @@ -8,7 +8,7 @@ class Driver: def __init__(self, network, processor): - self.async_events = simpy.Store(network.env) + self.processing_queue = simpy.Store(network.env) self.network = network self.env = network.env self.processor = processor @@ -26,7 +26,7 @@ def run(self): yield z while True: - event = yield self.async_events.get() + event = yield self.processing_queue.get() for z in self.issue_event(event[0], event[1]): if z: yield z @@ -62,7 +62,7 @@ def receive (self, msg_envelope): msg_envelope[1], msg_envelope[0], msg_envelope[2])) event = ['on_message', msg_envelope] - self.async_events.put(event) + self.processing_queue.put(event) def send (self, to_addr , msg): return self.network.send_unicast(self.address, to_addr, msg) diff --git a/simulator/peer.py b/simulator/peer.py index df979a3..019cf23 100644 --- a/simulator/peer.py +++ b/simulator/peer.py @@ -44,6 +44,5 @@ def on_advertise (self, msg): yield z def dds_test (self): - handle_controller = UniqueHandleController() # Criamos o gerador de identificadores. Se já existe, pegamos só uma referência the_service = DDS_Service(self.driver, handle_controller) # Iniciamos o serviço DDS diff --git a/simulator/simple_dds.py b/simulator/simple_dds.py index c0f6f73..60b35c4 100644 --- a/simulator/simple_dds.py +++ b/simulator/simple_dds.py @@ -57,31 +57,35 @@ def get_instance_handle(self): # Talvez seja necessário fazer dictionaries personalizados, que evitem problemas de acesso mútuo. class DDS_Service(Entity): - def __init__(self, driver, handle_controller): - self.handle_controller = handle_controller + def __init__(self, driver): + self.handle_controller = UniqueHandleController() self.instance_handle = self.handle_controller.generate_handle() self.driver = driver self.peer_list = [] - self.data_buffer = Data_Buffer() - self.local_changes = Data_Buffer() self.handles = {} # Handle: Entity self.participants = {} # Handle: Participant + self.local_participants = {} self.topics = {} # Topic name: Topic self.data_objects = {} # Handle: Data object self.message_handlers = {} + self.add_message_handler_methods() + self.attach_msg_reception_handler_to_driver() + self.discover_peers() + self.request_full_domain_data() + def set_instance_handle(self, handle): raise RuntimeError("DDS Service's handle cannot be changed.") def get_instance_handle(self): return self.instance_handle - # TODO: Notificar na rede quando houver atualização, para todos os métodos 'add' + def send_to_all_peers(self, msg): + self.driver.advertise(msg) - # Enfileiramos todas as mudanças locais, para mandá-las na rede de uma vez só. - def queue_local_modification(self, type_name, data): + def send_local_modification(self, type_name, data): change = (type_name, data) - self.local_changes.write_to(change) + self.send_to_all_peers(change) # Fazer verificação de handle duplicada def assign_handle(self, entity): @@ -93,13 +97,14 @@ def add_participant(self, participant): self.assign_handle(participant) handle = participant.get_instance_handle() self.participants[handle] = participant - self.queue_local_modification(('NEW_PARTICIPANT', participant)) + self.local_participants[handle] = participant + self.send_local_modification(('NEW_PARTICIPANT', participant)) def add_topic(self, topic): self.assign_handle(topic) topic_key = topic.get_name() self.topics[topic_key] = topic - self.queue_local_modification(('NEW_TOPIC', topic)) + self.send_local_modification(('NEW_TOPIC', topic)) def topic_exists(self, topic_name): return topic_name in self.topics @@ -109,13 +114,24 @@ def get_topic(self, topic_name): return self.topics[topic_name] else: # Como lidar com isto? pass + + def erase_topic_from_domain(self, topic): + # Deleta tópico e todos os dados associados a ele. + pass def discover_peers(self): self.peer_list = self.driver.fetch_peer_list() def receive_incoming_data(self, msg): - self.data_buffer.write_to(msg) logging.info(str(self.driver.get_time() + ' :: ' + f'Data received by DDS Service, handle {self.instance_handle}')) + self.interpret_data(msg) + + def add_message_handler_methods(self): + self.message_handlers['NEW_PARTICIPANT'] = self.append_remote_participant + self.message_handlers['NEW_TOPIC'] = self.append_remote_topic + self.message_handlers['NEW_DATA'] = self.update_data_object + self.message_handlers['SEND_ALL_DATA'] = self.send_full_domain_data + self.message_handlers['ALL_DATA'] = self.receive_full_domain_data def append_remote_participant(self, r_participant): handle = r_participant.get_instance_handle() @@ -123,7 +139,7 @@ def append_remote_participant(self, r_participant): self.handles[handle] = r_participant self.participants[handle] = r_participant - def append_remote_topics(self, r_topic): + def append_remote_topic(self, r_topic): topic_name = r_topic.get_name() if not self.topic_exists(topic_name): handle = r_topic.get_instance_handle() @@ -132,52 +148,59 @@ def append_remote_topics(self, r_topic): else: self.resolve_topic_conflict(r_topic) - def update_data_objects(self, r_data): + def update_data_object(self, r_data): handle = r_data.get_instance_handle() self.data_objects[handle] = r_data if handle not in self.handles: self.handles[handle] = r_data + self.send_data_object_to_all_participants(r_data) + + def send_data_object_to_all_participants(self, data_object): + for participant in self.local_participants.values(): + participant.update_all_subscribers(data_object) + + def send_full_domain_data(self, to_address): + local_data = [] + for key, value in self.participants.items(): + packet = ('NEW_PARTICIPANT', value) + local_data.append(packet) + for key, value in self.topics.items(): + packet = ('NEW_TOPIC', value) + local_data.append(packet) + for key, value in self.data_objects.items(): + packet = ('NEW_DATA', value) + local_data.append(packet) + msg = ('ALL_DATA', local_data) + self.driver.send(to_address, msg) + + def receive_full_domain_data(self, r_data): + for element in r_data: + self.interpret_data(element) def attach_msg_reception_handler_to_driver(self): self.driver.register_handler(self.receive_incoming_data, 'on_message') - # Não gosto muito deste nome. def interpret_data(self, data): # Presumimos que os dados estejam em uma 2-tupla, sendo o primeiro elemento.. # .. uma string descrevendo o pedido, o segundo elemento os dados em si - if data[1] not in self.message_handlers: + if data[0] not in self.message_handlers: logging.warning(str(self.driver.get_time() + ' :: ' + f'DDS Service (Handle {self.instance_handle}): Invalid request: {data[1]}')) else: - self.message_handlers[data[1]](data[2]) - - def process_received_messages(self): - message_queue = [] - self.data_buffer.read_from(message_queue) - for message in message_queue: - self.interpret_data(message) - - def propagate_local_changes(self): - if not self.local_changes.is_empty(): - changes_to_send = [] - self.local_changes.write_to(changes_to_send) - message = ('UPDATE', changes_to_send) - # Aqui, usaríamos o método advertise do driver, mas ele só lida com msgs tipo string. E agora? - # Como lidar com casos de problemas de conexão e falha de envio? Talvez .. - # .. mantê-las e tentar de novo, mas como implementar isso no simulador? - - def setup(self): - self.add_message_handler_methods() - self.attach_msg_reception_handler_to_driver() - self.discover_peers() + self.message_handlers[data[0]](data[1]) + + def request_full_domain_data(self): + request_msg = ('SEND_ALL_DATA', self.driver.address) + self.send_to_all_peers(request_msg) - def run(self): - # Através do driver, queremos: - # - Achar participantes - # - Atualizar instâncias de dados - # - Enviar dados relevantes aos subscribers - # - Processar dados recebidos - self.setup() + def retrieve_all_data_objects(self): + return self.data_objects.values() + def retrieve_filtered_data_objects(self, topic_name): + data = [] + for element in self.data_objects.values(): + if element.get_topic_name() == topic_name: + data.append(element) + return data class Domain_Participant(Entity): @@ -201,11 +224,11 @@ def delete_topic(self, topic): pass def find_topic(self, topic_name): - # Deve retornar um objeto do tipo Topic - pass + return self.service.get_topic(topic_name): def create_publisher(self, topic): - new_publisher = Publisher(self, topic) + data = self.service.retrieve_filtered_data_objects(topic.get_name()) + new_publisher = Publisher(self, topic, data) self.service.assign_handle(new_publisher) handle = new_publisher.get_instance_handle() self.publishers[handle] = new_publisher @@ -226,26 +249,49 @@ def get_discovered_participants(self): pass # Usar o service + def update_subscriber(self, subscriber, data_object): + subscriber.receive_data(data_object) + + def update_all_subscribers(self, data_object): + for subscriber in self.subscribers.values(): + subscriber.receive_data(data_object) + class Topic(Entity): def __init__(self, topic_name, participant): self.name = topic_name - self.parent = participant + self.participant = participant self.publishers = [] self.subscribers = [] def get_name(self): - pass + return self.name class Publisher(Entity): def __init__(self, participant, topic): - self.parent = participant + self.participant = participant self.topic = topic - self.data_buffer = [] def write(self, message): pass class Subscriber(Entity): - pass + + def __init__(self, participant, topic, data_objects): + self.participant = participant + self.topic = topic + self.available_data = data_objects + + def get_topic_name(self): + return self.topic.get_name() + +class Data_Object(Entity): + + def __init__(self, publisher_handle, topic, data): + self.publisher_handle = publisher_handle + self.topic = topic + self.content = data + + def get_topic_name(self): + return self.topic.get_name() \ No newline at end of file From bcc784404d37f14ffb2fee0465aec874957abf32 Mon Sep 17 00:00:00 2001 From: MA Date: Mon, 25 May 2020 19:27:48 -0300 Subject: [PATCH 07/12] First DDS test worked. --- simulator/dds_run.py | 52 ++++++++++++++++++ simulator/driver.py | 36 +++++++++--- simulator/network.py | 6 +- simulator/peer.py | 28 +++++++++- simulator/run.py | 2 +- simulator/simple_dds.py | 119 ++++++++++++++++++++++++++++++---------- 6 files changed, 201 insertions(+), 42 deletions(-) create mode 100644 simulator/dds_run.py diff --git a/simulator/dds_run.py b/simulator/dds_run.py new file mode 100644 index 0000000..8de013f --- /dev/null +++ b/simulator/dds_run.py @@ -0,0 +1,52 @@ +import simpy +import logging +import peer +import network +import driver +import processor +import simple_dds + +""" +Run app. +Peer control, duration and others details. + +""" +# Configuração do root logger +console_handler = logging.StreamHandler() +console_handler.setLevel(logging.INFO) +handlers = [console_handler] +logging.basicConfig(level = logging.INFO, + format = '[%(levelname)10s] [%(module)10s] %(message)s', + handlers = handlers +) + +NUM_PEERS = 1 +SIM_DURATION = 1000 + +# create env +env = simpy.Environment() + +# network +net = network.Network(env,2) + +#create peers + +nodes = [] + +teste = env.timeout(200) + + +proc_0 = processor.Processor(env, 0, 3) +dri_0 = driver.Driver(net, proc_0) +peer_0 = peer.Peer(dri_0, 0) +env.process(dri_0.run()) +env.process(peer_0.dds_write_test()) + +proc_1 = processor.Processor(env, 1, 3) +dri_1 = driver.Driver(net, proc_1) +peer_1 = peer.Peer(dri_1, 1) +env.process(dri_1.run()) +env.process(peer_1.dds_read_test()) + + +env.run(until=SIM_DURATION) diff --git a/simulator/driver.py b/simulator/driver.py index 29e08aa..3616e69 100644 --- a/simulator/driver.py +++ b/simulator/driver.py @@ -20,10 +20,12 @@ def __init__(self, network, processor): 'on_disconnect': [] } self.keep_alive_interval = 20 + self.async_calls = simpy.Store(network.env) def run(self): for z in self.connect(): yield z + self.env.process(self.execute_stored_calls()) while True: event = yield self.processing_queue.get() @@ -35,7 +37,7 @@ def run(self): # yield z def connect(self): - while True: # Tenta conectar-se repetidamente + while True: try: for z in self.network.register(self): yield z @@ -54,17 +56,18 @@ def disconnect(self): self.address = None def advertise(self, msg): - msg = '(ADV) '+str(msg) - return self.network.send_broadcast(self.address, msg) + # msg = '(ADV) '+str(msg) + for z in self.network.send_broadcast(self.address, msg): + yield z - def receive (self, msg_envelope): + def receive(self, msg_envelope): logging.info(str(self.env.now) + ' :: ' + '{} received from {}: {}'.format( - msg_envelope[1], msg_envelope[0], msg_envelope[2])) + msg_envelope[1], msg_envelope[0], str(msg_envelope[2]))) event = ['on_message', msg_envelope] self.processing_queue.put(event) - def send (self, to_addr , msg): + def send(self, to_addr , msg): return self.network.send_unicast(self.address, to_addr, msg) def register_handler (self, method, event='on_message'): @@ -83,4 +86,23 @@ def send_keepalive(self): self.network.renew(self.address) def get_time(self): - return self.env.now \ No newline at end of file + return self.env.now + + def async_function_call(self, call_info): + self.async_calls.put(call_info) + + def execute_stored_calls(self): + # TODO: Colocar bloco em função separada. + while True: + function_call = yield self.async_calls.get() + function_name = function_call[0] + # TODO: Mudar implementação para dictionary depois. + if function_name == 'send': + to_addr = function_call[1] + msg = function_call[2] + for z in self.send(to_addr, msg): + yield z + elif function_name == 'advertise': + msg = function_call[1] + for z in self.advertise(msg): + yield z \ No newline at end of file diff --git a/simulator/network.py b/simulator/network.py index 65b0b75..d0cd4b8 100644 --- a/simulator/network.py +++ b/simulator/network.py @@ -65,18 +65,20 @@ def send_unicast(self, from_addr, to_addr, msg): def send_broadcast(self, from_addr, msg): self.confirm_peer(from_addr) - logging.info(str(self.env.now) + ' :: ' + 'Message Broadcast from {} - {}'.format(from_addr,msg)) + logging.info(str(self.env.now) + ' :: ' + 'Message Broadcast from {} - {}'.format(from_addr, str(msg))) with self.node_list_access.request(priority=0) as nl_access: yield nl_access for addr in range(len(self.node_list)): to_addr = self.node_list[addr]['address'] + if to_addr == from_addr: + continue msg_envelope = [from_addr, to_addr, msg] with self.channel.request() as rec: yield rec node = self.node_list[addr]['node'] node.receive(msg_envelope) yield self.env.timeout(self.latency) - logging.info(str(self.env.now) + ' :: ' + 'Broadcast:'+ str(msg_envelope)) + #logging.info(str(self.env.now) + ' :: ' + 'Broadcast:'+ str(msg_envelope)) def find_next_available(self): addr = (self.next_available_address + 1) % self.max_hosts diff --git a/simulator/peer.py b/simulator/peer.py index 019cf23..cea1bb7 100644 --- a/simulator/peer.py +++ b/simulator/peer.py @@ -1,6 +1,6 @@ import logging -import simple_dds +from simple_dds import * """ Simulates the behavior of a peer in a network. @@ -43,6 +43,28 @@ def on_advertise (self, msg): for z in self.driver.advertise(msg): yield z - def dds_test (self): - the_service = DDS_Service(self.driver, handle_controller) # Iniciamos o serviço DDS + def dds_write_test (self): + yield self.driver.env.timeout(100) + print("write test") + the_service = DDS_Service(self.driver) + participant = Domain_Participant(the_service) + topic = participant.create_topic("TEST") + pub = participant.create_publisher(topic) + pub.write("Hello World!") + yield self.driver.env.timeout(300) + + def dds_read_test (self): + yield self.driver.env.timeout(150) + print("read test") + yield self.driver.env.timeout(10) + the_service = DDS_Service(self.driver) + participant = Domain_Participant(the_service) + n_topic = participant.create_topic("TEST") + sub = participant.create_subscriber(n_topic) + yield self.driver.env.timeout(17) + stuff = sub.read() + print(str(self.driver.env.now) + ':: ' + str(stuff)) + yield self.driver.env.timeout(300) + + diff --git a/simulator/run.py b/simulator/run.py index bdafd4b..32d0383 100644 --- a/simulator/run.py +++ b/simulator/run.py @@ -19,7 +19,7 @@ handlers = handlers ) -NUM_PEERS = 1 +NUM_PEERS = 2 SIM_DURATION = 300 # create env diff --git a/simulator/simple_dds.py b/simulator/simple_dds.py index 60b35c4..239a9fc 100644 --- a/simulator/simple_dds.py +++ b/simulator/simple_dds.py @@ -1,5 +1,6 @@ import logging from threading import Lock +from queue import * from singleton import Singleton @@ -8,7 +9,7 @@ class UniqueHandleController(metaclass=Singleton): def __init__(self): self.next_available_handle = 1 - self.lock = threading.Lock() + self.lock = Lock() def generate_handle(self): handle = None @@ -24,7 +25,7 @@ class Data_Buffer: def __init__(self): self.data_buffer = [] - self.lock = threading.Lock() + self.lock = Lock() def write_to(self, data): with self.lock: @@ -81,7 +82,7 @@ def get_instance_handle(self): return self.instance_handle def send_to_all_peers(self, msg): - self.driver.advertise(msg) + self.driver.async_function_call(['advertise', msg]) def send_local_modification(self, type_name, data): change = (type_name, data) @@ -98,13 +99,20 @@ def add_participant(self, participant): handle = participant.get_instance_handle() self.participants[handle] = participant self.local_participants[handle] = participant - self.send_local_modification(('NEW_PARTICIPANT', participant)) + self.send_local_modification('NEW_PARTICIPANT', participant) def add_topic(self, topic): self.assign_handle(topic) topic_key = topic.get_name() self.topics[topic_key] = topic - self.send_local_modification(('NEW_TOPIC', topic)) + self.send_local_modification('NEW_TOPIC', topic) + + def add_data_object(self, data_object): + self.assign_handle(data_object) + handle = data_object.get_instance_handle() + self.data_objects[handle] = data_object + self.attach_data_object_to_topic(data_object) + self.send_local_modification('NEW_DATA', data_object) def topic_exists(self, topic_name): return topic_name in self.topics @@ -123,13 +131,14 @@ def discover_peers(self): self.peer_list = self.driver.fetch_peer_list() def receive_incoming_data(self, msg): - logging.info(str(self.driver.get_time() + ' :: ' + f'Data received by DDS Service, handle {self.instance_handle}')) - self.interpret_data(msg) + logging.info(str(self.driver.get_time()) + ' :: ' + f'Data received by DDS Service, handle {str(self.instance_handle)}') + for z in self.interpret_data(msg): + yield z def add_message_handler_methods(self): self.message_handlers['NEW_PARTICIPANT'] = self.append_remote_participant self.message_handlers['NEW_TOPIC'] = self.append_remote_topic - self.message_handlers['NEW_DATA'] = self.update_data_object + self.message_handlers['NEW_DATA'] = self.append_data_object self.message_handlers['SEND_ALL_DATA'] = self.send_full_domain_data self.message_handlers['ALL_DATA'] = self.receive_full_domain_data @@ -148,17 +157,29 @@ def append_remote_topic(self, r_topic): else: self.resolve_topic_conflict(r_topic) - def update_data_object(self, r_data): - handle = r_data.get_instance_handle() - self.data_objects[handle] = r_data + def resolve_topic_conflict(self, topic): + # TODO: Completar este método. + # O tópico com a instance handle menor tem prioridade. + # Caso o serviço local tenha prioridade, é necessário informar os outros nodos. + pass + + def append_data_object(self, new_data): + handle = new_data.get_instance_handle() + self.data_objects[handle] = new_data if handle not in self.handles: - self.handles[handle] = r_data - self.send_data_object_to_all_participants(r_data) + self.handles[handle] = new_data + self.send_data_object_to_all_participants(new_data) + self.attach_data_object_to_topic(new_data) def send_data_object_to_all_participants(self, data_object): for participant in self.local_participants.values(): participant.update_all_subscribers(data_object) + def attach_data_object_to_topic(self, data_object): + topic_name = data_object.get_topic_name() + if self.topic_exists(topic_name): + self.topics[topic_name].attach_data_object(data_object) + def send_full_domain_data(self, to_address): local_data = [] for key, value in self.participants.items(): @@ -171,22 +192,29 @@ def send_full_domain_data(self, to_address): packet = ('NEW_DATA', value) local_data.append(packet) msg = ('ALL_DATA', local_data) - self.driver.send(to_address, msg) + self.driver.async_function_call(['send', to_address, msg]) def receive_full_domain_data(self, r_data): + # TODO: Remove this hack. + # HACK ALERT I JUST WANT THIS TO WORK for element in r_data: - self.interpret_data(element) + envelope = [0,0, element] + for z in self.interpret_data(envelope): + z - def attach_msg_reception_handler_to_driver(self): - self.driver.register_handler(self.receive_incoming_data, 'on_message') - - def interpret_data(self, data): + def interpret_data(self, msg): + #breakpoint() + data = msg[2] # Presumimos que os dados estejam em uma 2-tupla, sendo o primeiro elemento.. # .. uma string descrevendo o pedido, o segundo elemento os dados em si if data[0] not in self.message_handlers: - logging.warning(str(self.driver.get_time() + ' :: ' + f'DDS Service (Handle {self.instance_handle}): Invalid request: {data[1]}')) + logging.warning(str(self.driver.get_time()) + ' :: ' + f'DDS Service (Handle {str(self.instance_handle)}): Invalid request: {str(data[1])}') else: self.message_handlers[data[0]](data[1]) + yield self.driver.env.timeout(0) + + def attach_msg_reception_handler_to_driver(self): + self.driver.register_handler(self.receive_incoming_data, 'on_message') def request_full_domain_data(self): request_msg = ('SEND_ALL_DATA', self.driver.address) @@ -205,42 +233,47 @@ def retrieve_filtered_data_objects(self, topic_name): class Domain_Participant(Entity): def __init__(self, dds_service): + super(Domain_Participant, self).__init__() self.service = dds_service self.publishers = {} self.subscribers = {} self.topics = {} - self.dds_service.add_participant(self) + self.service.add_participant(self) def create_topic(self, topic_name): if self.service.topic_exists(topic_name): logging.warning(f'{topic_name} already exists.') + return None else: new_topic = Topic(topic_name, self) self.service.add_topic(new_topic) - self.topics.append(new_topic) + self.topics[topic_name] = new_topic + return new_topic def delete_topic(self, topic): # Pré-condição: tópico deve ter sido criado por este participante pass def find_topic(self, topic_name): - return self.service.get_topic(topic_name): + return self.service.get_topic(topic_name) def create_publisher(self, topic): - data = self.service.retrieve_filtered_data_objects(topic.get_name()) - new_publisher = Publisher(self, topic, data) + new_publisher = Publisher(self, topic) self.service.assign_handle(new_publisher) handle = new_publisher.get_instance_handle() self.publishers[handle] = new_publisher + return new_publisher def delete_publisher(self, publisher): pass def create_subscriber(self, topic): - new_subscriber = Subscriber(self, topic) + data = self.service.retrieve_filtered_data_objects(topic.get_name()) + new_subscriber = Subscriber(self, topic, data) self.service.assign_handle(new_subscriber) handle = new_subscriber.get_instance_handle() self.subscribers[handle] = new_subscriber + return new_subscriber def delete_subscriber(self, subscriber): pass @@ -259,39 +292,67 @@ def update_all_subscribers(self, data_object): class Topic(Entity): def __init__(self, topic_name, participant): + super(Topic, self).__init__() self.name = topic_name self.participant = participant self.publishers = [] self.subscribers = [] + self.data_objects = {} def get_name(self): return self.name + def attach_data_object(self, data_object): + if data_object.get_topic_name() == self.name: + handle = data_object.get_instance_handle() + self.data_objects[handle] = data_object + class Publisher(Entity): def __init__(self, participant, topic): + super(Publisher, self).__init__() self.participant = participant self.topic = topic - def write(self, message): - pass + def write(self, data): + pub_handle = self.get_instance_handle() + new_data = Data_Object(pub_handle, self.topic, data) + self.participant.service.add_data_object(new_data) class Subscriber(Entity): def __init__(self, participant, topic, data_objects): + super(Subscriber, self).__init__() self.participant = participant self.topic = topic - self.available_data = data_objects + self.available_data = Queue() + for element in data_objects: + self.available_data.put(element) def get_topic_name(self): return self.topic.get_name() + def receive_data(self, data_object): + self.available_data.put(data_object) + + def read(self): + try: + data_object = self.available_data.get(block=False) + return data_object + except Empty: + logging.debug('No data objects available') + return None + class Data_Object(Entity): def __init__(self, publisher_handle, topic, data): + super(Data_Object, self).__init__() self.publisher_handle = publisher_handle self.topic = topic self.content = data + def __str__(self): + return self.content + def get_topic_name(self): return self.topic.get_name() \ No newline at end of file From 1d989e3a575bec35650529a976afb1b977c41b82 Mon Sep 17 00:00:00 2001 From: MA Date: Tue, 26 May 2020 16:21:11 -0300 Subject: [PATCH 08/12] Split simple_dds module. --- simulator/dds_run.py | 1 - simulator/peer.py | 10 +- simulator/simple_dds/__init__.py | 2 + simulator/simple_dds/data_object.py | 15 ++ .../dds_service.py} | 150 +----------------- simulator/simple_dds/domain_participant.py | 63 ++++++++ simulator/simple_dds/entity.py | 15 ++ simulator/simple_dds/publisher.py | 14 ++ simulator/simple_dds/subscriber.py | 30 ++++ simulator/simple_dds/topic.py | 19 +++ 10 files changed, 165 insertions(+), 154 deletions(-) create mode 100644 simulator/simple_dds/__init__.py create mode 100644 simulator/simple_dds/data_object.py rename simulator/{simple_dds.py => simple_dds/dds_service.py} (64%) create mode 100644 simulator/simple_dds/domain_participant.py create mode 100644 simulator/simple_dds/entity.py create mode 100644 simulator/simple_dds/publisher.py create mode 100644 simulator/simple_dds/subscriber.py create mode 100644 simulator/simple_dds/topic.py diff --git a/simulator/dds_run.py b/simulator/dds_run.py index 8de013f..14a1bfe 100644 --- a/simulator/dds_run.py +++ b/simulator/dds_run.py @@ -4,7 +4,6 @@ import network import driver import processor -import simple_dds """ Run app. diff --git a/simulator/peer.py b/simulator/peer.py index cea1bb7..c745549 100644 --- a/simulator/peer.py +++ b/simulator/peer.py @@ -1,5 +1,4 @@ import logging - from simple_dds import * """ @@ -46,8 +45,8 @@ def on_advertise (self, msg): def dds_write_test (self): yield self.driver.env.timeout(100) print("write test") - the_service = DDS_Service(self.driver) - participant = Domain_Participant(the_service) + the_service = dds_service.DDS_Service(self.driver) + participant = domain_participant.Domain_Participant(the_service) topic = participant.create_topic("TEST") pub = participant.create_publisher(topic) pub.write("Hello World!") @@ -56,9 +55,8 @@ def dds_write_test (self): def dds_read_test (self): yield self.driver.env.timeout(150) print("read test") - yield self.driver.env.timeout(10) - the_service = DDS_Service(self.driver) - participant = Domain_Participant(the_service) + the_service = dds_service.DDS_Service(self.driver) + participant = domain_participant.Domain_Participant(the_service) n_topic = participant.create_topic("TEST") sub = participant.create_subscriber(n_topic) yield self.driver.env.timeout(17) diff --git a/simulator/simple_dds/__init__.py b/simulator/simple_dds/__init__.py new file mode 100644 index 0000000..c2e948e --- /dev/null +++ b/simulator/simple_dds/__init__.py @@ -0,0 +1,2 @@ +__all__ = ["entity", "dds_service", "domain_participant", "publisher", "subscriber", +"data_object", "topic"] \ No newline at end of file diff --git a/simulator/simple_dds/data_object.py b/simulator/simple_dds/data_object.py new file mode 100644 index 0000000..e28606f --- /dev/null +++ b/simulator/simple_dds/data_object.py @@ -0,0 +1,15 @@ +from simple_dds import entity + +class Data_Object(entity.Entity): + + def __init__(self, publisher_handle, topic, data): + super(Data_Object, self).__init__() + self.publisher_handle = publisher_handle + self.topic = topic + self.content = data + + def __str__(self): + return self.content + + def get_topic_name(self): + return self.topic.get_name() \ No newline at end of file diff --git a/simulator/simple_dds.py b/simulator/simple_dds/dds_service.py similarity index 64% rename from simulator/simple_dds.py rename to simulator/simple_dds/dds_service.py index 239a9fc..ceb317d 100644 --- a/simulator/simple_dds.py +++ b/simulator/simple_dds/dds_service.py @@ -1,8 +1,7 @@ import logging from threading import Lock -from queue import * - from singleton import Singleton +from simple_dds import entity # Possíveis problemas de uso mútuo nesta classe? Veremos quando testarmos. class UniqueHandleController(metaclass=Singleton): @@ -39,24 +38,8 @@ def read_from(self, container): def is_empty(self): return len(self.data_buffer) == 0 -class Entity: - - def __init__(self): - self.instance_handle = 0 - - def set_instance_handle(self, handle): - if self.instance_handle != 0: - raise RuntimeError("DDS Instance already has a handle.") - else: - self.instance_handle = handle - - def get_instance_handle(self): - if self.instance_handle == 0: - raise RuntimeError("DDS Instance has not been assigned a handle") - return self.instance_handle - # Talvez seja necessário fazer dictionaries personalizados, que evitem problemas de acesso mútuo. -class DDS_Service(Entity): +class DDS_Service(entity.Entity): def __init__(self, driver): self.handle_controller = UniqueHandleController() @@ -228,131 +211,4 @@ def retrieve_filtered_data_objects(self, topic_name): for element in self.data_objects.values(): if element.get_topic_name() == topic_name: data.append(element) - return data - -class Domain_Participant(Entity): - - def __init__(self, dds_service): - super(Domain_Participant, self).__init__() - self.service = dds_service - self.publishers = {} - self.subscribers = {} - self.topics = {} - self.service.add_participant(self) - - def create_topic(self, topic_name): - if self.service.topic_exists(topic_name): - logging.warning(f'{topic_name} already exists.') - return None - else: - new_topic = Topic(topic_name, self) - self.service.add_topic(new_topic) - self.topics[topic_name] = new_topic - return new_topic - - def delete_topic(self, topic): - # Pré-condição: tópico deve ter sido criado por este participante - pass - - def find_topic(self, topic_name): - return self.service.get_topic(topic_name) - - def create_publisher(self, topic): - new_publisher = Publisher(self, topic) - self.service.assign_handle(new_publisher) - handle = new_publisher.get_instance_handle() - self.publishers[handle] = new_publisher - return new_publisher - - def delete_publisher(self, publisher): - pass - - def create_subscriber(self, topic): - data = self.service.retrieve_filtered_data_objects(topic.get_name()) - new_subscriber = Subscriber(self, topic, data) - self.service.assign_handle(new_subscriber) - handle = new_subscriber.get_instance_handle() - self.subscribers[handle] = new_subscriber - return new_subscriber - - def delete_subscriber(self, subscriber): - pass - - def get_discovered_participants(self): - pass - # Usar o service - - def update_subscriber(self, subscriber, data_object): - subscriber.receive_data(data_object) - - def update_all_subscribers(self, data_object): - for subscriber in self.subscribers.values(): - subscriber.receive_data(data_object) - -class Topic(Entity): - - def __init__(self, topic_name, participant): - super(Topic, self).__init__() - self.name = topic_name - self.participant = participant - self.publishers = [] - self.subscribers = [] - self.data_objects = {} - - def get_name(self): - return self.name - - def attach_data_object(self, data_object): - if data_object.get_topic_name() == self.name: - handle = data_object.get_instance_handle() - self.data_objects[handle] = data_object - -class Publisher(Entity): - - def __init__(self, participant, topic): - super(Publisher, self).__init__() - self.participant = participant - self.topic = topic - - def write(self, data): - pub_handle = self.get_instance_handle() - new_data = Data_Object(pub_handle, self.topic, data) - self.participant.service.add_data_object(new_data) - -class Subscriber(Entity): - - def __init__(self, participant, topic, data_objects): - super(Subscriber, self).__init__() - self.participant = participant - self.topic = topic - self.available_data = Queue() - for element in data_objects: - self.available_data.put(element) - - def get_topic_name(self): - return self.topic.get_name() - - def receive_data(self, data_object): - self.available_data.put(data_object) - - def read(self): - try: - data_object = self.available_data.get(block=False) - return data_object - except Empty: - logging.debug('No data objects available') - return None - -class Data_Object(Entity): - - def __init__(self, publisher_handle, topic, data): - super(Data_Object, self).__init__() - self.publisher_handle = publisher_handle - self.topic = topic - self.content = data - - def __str__(self): - return self.content - - def get_topic_name(self): - return self.topic.get_name() \ No newline at end of file + return data \ No newline at end of file diff --git a/simulator/simple_dds/domain_participant.py b/simulator/simple_dds/domain_participant.py new file mode 100644 index 0000000..f3ef23d --- /dev/null +++ b/simulator/simple_dds/domain_participant.py @@ -0,0 +1,63 @@ +from simple_dds import entity +from simple_dds import topic +from simple_dds import publisher +from simple_dds import subscriber + +class Domain_Participant(entity.Entity): + + def __init__(self, dds_service): + super(Domain_Participant, self).__init__() + self.service = dds_service + self.publishers = {} + self.subscribers = {} + self.topics = {} + self.service.add_participant(self) + + def create_topic(self, topic_name): + if self.service.topic_exists(topic_name): + logging.warning(f'{topic_name} already exists.') + return None + else: + new_topic = topic.Topic(topic_name, self) + self.service.add_topic(new_topic) + self.topics[topic_name] = new_topic + return new_topic + + def delete_topic(self, topic): + # Pré-condição: tópico deve ter sido criado por este participante + pass + + def find_topic(self, topic_name): + return self.service.get_topic(topic_name) + + def create_publisher(self, topic): + new_publisher = publisher.Publisher(self, topic) + self.service.assign_handle(new_publisher) + handle = new_publisher.get_instance_handle() + self.publishers[handle] = new_publisher + return new_publisher + + def delete_publisher(self, publisher): + pass + + def create_subscriber(self, topic): + data = self.service.retrieve_filtered_data_objects(topic.get_name()) + new_subscriber = subscriber.Subscriber(self, topic, data) + self.service.assign_handle(new_subscriber) + handle = new_subscriber.get_instance_handle() + self.subscribers[handle] = new_subscriber + return new_subscriber + + def delete_subscriber(self, subscriber): + pass + + def get_discovered_participants(self): + pass + # Usar o service + + def update_subscriber(self, subscriber, data_object): + subscriber.receive_data(data_object) + + def update_all_subscribers(self, data_object): + for subscriber in self.subscribers.values(): + subscriber.receive_data(data_object) \ No newline at end of file diff --git a/simulator/simple_dds/entity.py b/simulator/simple_dds/entity.py new file mode 100644 index 0000000..ac01822 --- /dev/null +++ b/simulator/simple_dds/entity.py @@ -0,0 +1,15 @@ +class Entity: + + def __init__(self): + self.instance_handle = 0 + + def set_instance_handle(self, handle): + if self.instance_handle != 0: + raise RuntimeError("DDS Instance already has a handle.") + else: + self.instance_handle = handle + + def get_instance_handle(self): + if self.instance_handle == 0: + raise RuntimeError("DDS Instance has not been assigned a handle") + return self.instance_handle \ No newline at end of file diff --git a/simulator/simple_dds/publisher.py b/simulator/simple_dds/publisher.py new file mode 100644 index 0000000..62333a8 --- /dev/null +++ b/simulator/simple_dds/publisher.py @@ -0,0 +1,14 @@ +from simple_dds import entity +from simple_dds import data_object + +class Publisher(entity.Entity): + + def __init__(self, participant, topic): + super(Publisher, self).__init__() + self.participant = participant + self.topic = topic + + def write(self, data): + pub_handle = self.get_instance_handle() + new_data = data_object.Data_Object(pub_handle, self.topic, data) + self.participant.service.add_data_object(new_data) \ No newline at end of file diff --git a/simulator/simple_dds/subscriber.py b/simulator/simple_dds/subscriber.py new file mode 100644 index 0000000..16444d1 --- /dev/null +++ b/simulator/simple_dds/subscriber.py @@ -0,0 +1,30 @@ +import logging +from queue import * + +from simple_dds import entity + +# TODO: Adicionar suporte para chamada de listener (callback). +class Subscriber(entity.Entity): + + def __init__(self, participant, topic, data_objects): + super(Subscriber, self).__init__() + self.participant = participant + self.topic = topic + self.available_data = Queue() + for element in data_objects: + self.available_data.put(element) + + def get_topic_name(self): + return self.topic.get_name() + + def receive_data(self, data_object): + if data_object.get_topic_name() == self.topic.get_name(): + self.available_data.put(data_object) + + def read(self): + try: + data_object = self.available_data.get(block=False) + return data_object + except Empty: + logging.debug('No data objects available') + return None \ No newline at end of file diff --git a/simulator/simple_dds/topic.py b/simulator/simple_dds/topic.py new file mode 100644 index 0000000..c9da288 --- /dev/null +++ b/simulator/simple_dds/topic.py @@ -0,0 +1,19 @@ +from simple_dds import entity + +class Topic(entity.Entity): + + def __init__(self, topic_name, participant): + super(Topic, self).__init__() + self.name = topic_name + self.participant = participant + self.publishers = [] + self.subscribers = [] + self.data_objects = {} + + def get_name(self): + return self.name + + def attach_data_object(self, data_object): + if data_object.get_topic_name() == self.name: + handle = data_object.get_instance_handle() + self.data_objects[handle] = data_object \ No newline at end of file From 4fbf256d9711ec0fcab2a80cdaa1d457e8319c0e Mon Sep 17 00:00:00 2001 From: MA Date: Mon, 1 Jun 2020 20:35:10 -0300 Subject: [PATCH 09/12] Added initial DDS tests. --- simulator/dds_run.py | 4 +- simulator/driver.py | 6 +- simulator/{mockup.py => mockup.txt} | 1 - simulator/network.py | 8 +- simulator/peer.py | 29 ++++-- simulator/simple_dds/data_object.py | 4 +- simulator/simple_dds/dds_service.py | 108 ++++++++++----------- simulator/simple_dds/domain_participant.py | 7 +- simulator/simple_dds/entity.py | 2 +- simulator/simple_dds/subscriber.py | 7 +- tests/simulator/__init__.py | 0 tests/simulator/test_dds.py | 92 ++++++++++++++++++ tests/simulator/test_initial_connection.py | 14 ++- 13 files changed, 192 insertions(+), 90 deletions(-) rename simulator/{mockup.py => mockup.txt} (99%) delete mode 100644 tests/simulator/__init__.py create mode 100644 tests/simulator/test_dds.py diff --git a/simulator/dds_run.py b/simulator/dds_run.py index 14a1bfe..be3d143 100644 --- a/simulator/dds_run.py +++ b/simulator/dds_run.py @@ -15,7 +15,7 @@ console_handler.setLevel(logging.INFO) handlers = [console_handler] logging.basicConfig(level = logging.INFO, - format = '[%(levelname)10s] [%(module)10s] %(message)s', + format = '[%(levelname)10s] [%(module)12s] %(message)s', handlers = handlers ) @@ -39,7 +39,7 @@ dri_0 = driver.Driver(net, proc_0) peer_0 = peer.Peer(dri_0, 0) env.process(dri_0.run()) -env.process(peer_0.dds_write_test()) +env.process(peer_0.wait_then_publish_message('TEST', 'Hello World!', 100)) proc_1 = processor.Processor(env, 1, 3) dri_1 = driver.Driver(net, proc_1) diff --git a/simulator/driver.py b/simulator/driver.py index 3616e69..aa75963 100644 --- a/simulator/driver.py +++ b/simulator/driver.py @@ -3,8 +3,6 @@ import simpy import logging -import custom_error - class Driver: def __init__(self, network, processor): @@ -44,7 +42,7 @@ def connect(self): for z in self.issue_event('on_connect', self.address): yield z break # Se chegarmos aqui, código completado com sucesso, saímos do loop - except RegistrationError as err: + except ConnectionError as err: print(err.message) yield self.env.timeout(1) @@ -52,11 +50,9 @@ def fetch_peer_list(self): return self.network.send_addresses(self) def disconnect(self): - former_address = self.address self.address = None def advertise(self, msg): - # msg = '(ADV) '+str(msg) for z in self.network.send_broadcast(self.address, msg): yield z diff --git a/simulator/mockup.py b/simulator/mockup.txt similarity index 99% rename from simulator/mockup.py rename to simulator/mockup.txt index 58c7f7e..f2cf479 100644 --- a/simulator/mockup.py +++ b/simulator/mockup.txt @@ -1,5 +1,4 @@ import simpy -import logging import customdds diff --git a/simulator/network.py b/simulator/network.py index d0cd4b8..1b180e1 100644 --- a/simulator/network.py +++ b/simulator/network.py @@ -2,8 +2,6 @@ import simpy import logging -import custom_error - class Network: def __init__(self, env, latency, max_hosts = 100): @@ -25,7 +23,7 @@ def register(self, node_driver): yield rec if self.full_capacity: logging.warning(str(self.env.now) + ' :: ' + 'Could not register node: Network at full capacity') - raise RegistrationError("Network at full capacity") + raise ConnectionError("Network at full capacity") else: curr_address = self.next_available_address logging.info(str(self.env.now) + ' :: ' + 'connecting {}'.format(curr_address)) @@ -122,9 +120,9 @@ def send_addresses(self, driver): if peer['address'] is not driver.address: addr_list.append(peer['address']) return addr_list - + def dhcp(self): while True: for z in self.check_lease(): yield z - yield self.env.timeout(1) + yield self.env.timeout(1) \ No newline at end of file diff --git a/simulator/peer.py b/simulator/peer.py index c745549..4496529 100644 --- a/simulator/peer.py +++ b/simulator/peer.py @@ -25,6 +25,7 @@ def __init__(self, driver, id): self.driver.register_handler(self.on_advertise, 'on_advertise') self.driver.register_handler(self.on_disconnect, 'on_disconnect') self.name = 'peer_{}'.format(id) + self.latest_read_msg = 0 def on_message (self, msg): logging.info(str(self.driver.env.now) + ' :: ' + '{} received msg: {}'.format(self.name, msg)) @@ -42,16 +43,26 @@ def on_advertise (self, msg): for z in self.driver.advertise(msg): yield z - def dds_write_test (self): - yield self.driver.env.timeout(100) - print("write test") + # TODO: O nome não é adequado: faz mais do que publicar mensagem, antes cria objetos.. + # .. necessários. É preciso mudar depois. + def wait_then_publish_message(self, topic_name, message, wait_time=100): + yield self.driver.env.timeout(wait_time) the_service = dds_service.DDS_Service(self.driver) participant = domain_participant.Domain_Participant(the_service) - topic = participant.create_topic("TEST") + topic = participant.create_topic(topic_name) pub = participant.create_publisher(topic) - pub.write("Hello World!") - yield self.driver.env.timeout(300) - + pub.write(message) + + def wait_then_read_message(self, topic_name, message, wait_time=100): + yield self.driver.env.timeout(wait_time) + the_service = dds_service.DDS_Service(self.driver) + participant = domain_participant.Domain_Participant(the_service) + topic = participant.create_topic(topic_name) + sub = participant.create_subscriber(topic) + # Atenção à linha a seguir. Talvez seja necessário alterar o valor mais tarde. + yield self.driver.env.timeout(17) # Tempo para recebimento de mensagens de outros peers contendo dados do domínio. + self.latest_read_msg = sub.read() + def dds_read_test (self): yield self.driver.env.timeout(150) print("read test") @@ -62,7 +73,9 @@ def dds_read_test (self): yield self.driver.env.timeout(17) stuff = sub.read() print(str(self.driver.env.now) + ':: ' + str(stuff)) - yield self.driver.env.timeout(300) + + def read_new_message(self, subscriber): + self.latest_read_msg = subscriber.read() diff --git a/simulator/simple_dds/data_object.py b/simulator/simple_dds/data_object.py index e28606f..e7970e4 100644 --- a/simulator/simple_dds/data_object.py +++ b/simulator/simple_dds/data_object.py @@ -1,7 +1,7 @@ from simple_dds import entity class Data_Object(entity.Entity): - + def __init__(self, publisher_handle, topic, data): super(Data_Object, self).__init__() self.publisher_handle = publisher_handle @@ -9,7 +9,7 @@ def __init__(self, publisher_handle, topic, data): self.content = data def __str__(self): - return self.content + return str(self.content) def get_topic_name(self): return self.topic.get_name() \ No newline at end of file diff --git a/simulator/simple_dds/dds_service.py b/simulator/simple_dds/dds_service.py index ceb317d..9ef627c 100644 --- a/simulator/simple_dds/dds_service.py +++ b/simulator/simple_dds/dds_service.py @@ -9,7 +9,7 @@ class UniqueHandleController(metaclass=Singleton): def __init__(self): self.next_available_handle = 1 self.lock = Lock() - + def generate_handle(self): handle = None with self.lock: @@ -53,10 +53,10 @@ def __init__(self, driver): self.data_objects = {} # Handle: Data object self.message_handlers = {} - self.add_message_handler_methods() - self.attach_msg_reception_handler_to_driver() - self.discover_peers() - self.request_full_domain_data() + self._add_message_handler_methods() + self._attach_msg_reception_handler_to_driver() + self._discover_peers() + self._request_full_domain_data() def set_instance_handle(self, handle): raise RuntimeError("DDS Service's handle cannot be changed.") @@ -64,12 +64,12 @@ def set_instance_handle(self, handle): def get_instance_handle(self): return self.instance_handle - def send_to_all_peers(self, msg): + def _send_to_all_peers(self, msg): self.driver.async_function_call(['advertise', msg]) - def send_local_modification(self, type_name, data): + def _send_local_modification(self, type_name, data): change = (type_name, data) - self.send_to_all_peers(change) + self._send_to_all_peers(change) # Fazer verificação de handle duplicada def assign_handle(self, entity): @@ -82,20 +82,20 @@ def add_participant(self, participant): handle = participant.get_instance_handle() self.participants[handle] = participant self.local_participants[handle] = participant - self.send_local_modification('NEW_PARTICIPANT', participant) + self._send_local_modification('NEW_PARTICIPANT', participant) def add_topic(self, topic): self.assign_handle(topic) topic_key = topic.get_name() self.topics[topic_key] = topic - self.send_local_modification('NEW_TOPIC', topic) + self._send_local_modification('NEW_TOPIC', topic) def add_data_object(self, data_object): self.assign_handle(data_object) handle = data_object.get_instance_handle() self.data_objects[handle] = data_object - self.attach_data_object_to_topic(data_object) - self.send_local_modification('NEW_DATA', data_object) + self._attach_data_object_to_topic(data_object) + self._send_local_modification('NEW_DATA', data_object) def topic_exists(self, topic_name): return topic_name in self.topics @@ -106,102 +106,102 @@ def get_topic(self, topic_name): else: # Como lidar com isto? pass - def erase_topic_from_domain(self, topic): + def _erase_topic_from_domain(self, topic): # Deleta tópico e todos os dados associados a ele. pass - def discover_peers(self): + def _discover_peers(self): self.peer_list = self.driver.fetch_peer_list() - - def receive_incoming_data(self, msg): - logging.info(str(self.driver.get_time()) + ' :: ' + f'Data received by DDS Service, handle {str(self.instance_handle)}') - for z in self.interpret_data(msg): - yield z - def add_message_handler_methods(self): - self.message_handlers['NEW_PARTICIPANT'] = self.append_remote_participant - self.message_handlers['NEW_TOPIC'] = self.append_remote_topic - self.message_handlers['NEW_DATA'] = self.append_data_object - self.message_handlers['SEND_ALL_DATA'] = self.send_full_domain_data - self.message_handlers['ALL_DATA'] = self.receive_full_domain_data + def _add_message_handler_methods(self): + self.message_handlers['NEW_PARTICIPANT'] = self._append_remote_participant + self.message_handlers['NEW_TOPIC'] = self._append_remote_topic + self.message_handlers['NEW_DATA'] = self._append_data_object + self.message_handlers['SEND_ALL_DATA'] = self._send_full_domain_data + self.message_handlers['ALL_DATA'] = self._receive_full_domain_data - def append_remote_participant(self, r_participant): + def _append_remote_participant(self, r_participant): handle = r_participant.get_instance_handle() if handle not in self.participants and handle not in self.handles: self.handles[handle] = r_participant self.participants[handle] = r_participant - def append_remote_topic(self, r_topic): + def _append_remote_topic(self, r_topic): topic_name = r_topic.get_name() if not self.topic_exists(topic_name): handle = r_topic.get_instance_handle() self.handles[handle] = r_topic self.topics[topic_name] = r_topic else: - self.resolve_topic_conflict(r_topic) + self._resolve_topic_conflict(r_topic) - def resolve_topic_conflict(self, topic): + def _resolve_topic_conflict(self, topic): # TODO: Completar este método. # O tópico com a instance handle menor tem prioridade. # Caso o serviço local tenha prioridade, é necessário informar os outros nodos. pass - def append_data_object(self, new_data): + def _append_data_object(self, new_data): handle = new_data.get_instance_handle() self.data_objects[handle] = new_data if handle not in self.handles: self.handles[handle] = new_data - self.send_data_object_to_all_participants(new_data) - self.attach_data_object_to_topic(new_data) + self._send_data_object_to_all_participants(new_data) + self._attach_data_object_to_topic(new_data) - def send_data_object_to_all_participants(self, data_object): + def _send_data_object_to_all_participants(self, data_object): for participant in self.local_participants.values(): participant.update_all_subscribers(data_object) - def attach_data_object_to_topic(self, data_object): + def _attach_data_object_to_topic(self, data_object): topic_name = data_object.get_topic_name() if self.topic_exists(topic_name): self.topics[topic_name].attach_data_object(data_object) - def send_full_domain_data(self, to_address): + def _send_full_domain_data(self, to_address): local_data = [] - for key, value in self.participants.items(): - packet = ('NEW_PARTICIPANT', value) + for participant in self.participants.values(): + packet = ('NEW_PARTICIPANT', participant) local_data.append(packet) - for key, value in self.topics.items(): - packet = ('NEW_TOPIC', value) + for topic in self.topics.values(): + packet = ('NEW_TOPIC', topic) local_data.append(packet) - for key, value in self.data_objects.items(): - packet = ('NEW_DATA', value) + for data_object in self.data_objects.values(): + packet = ('NEW_DATA', data_object) local_data.append(packet) msg = ('ALL_DATA', local_data) self.driver.async_function_call(['send', to_address, msg]) - def receive_full_domain_data(self, r_data): - # TODO: Remove this hack. - # HACK ALERT I JUST WANT THIS TO WORK + def _receive_full_domain_data(self, r_data): for element in r_data: - envelope = [0,0, element] - for z in self.interpret_data(envelope): - z + self._interpret_data(element) - def interpret_data(self, msg): - #breakpoint() + def _unpack_data(self, msg): + # Formato esperado da mensagem: + # [0] Remetente; [1] Destinatário; [2] Mensagem em si data = msg[2] + self._interpret_data(data) + yield self.driver.env.timeout(0) + + def _interpret_data(self, data): # Presumimos que os dados estejam em uma 2-tupla, sendo o primeiro elemento.. # .. uma string descrevendo o pedido, o segundo elemento os dados em si if data[0] not in self.message_handlers: logging.warning(str(self.driver.get_time()) + ' :: ' + f'DDS Service (Handle {str(self.instance_handle)}): Invalid request: {str(data[1])}') else: self.message_handlers[data[0]](data[1]) - yield self.driver.env.timeout(0) - def attach_msg_reception_handler_to_driver(self): - self.driver.register_handler(self.receive_incoming_data, 'on_message') + def _attach_msg_reception_handler_to_driver(self): + self.driver.register_handler(self._receive_incoming_data, 'on_message') + + def _receive_incoming_data(self, msg): + logging.info(str(self.driver.get_time()) + ' :: ' + f'Data received by DDS Service, handle {str(self.instance_handle)}') + for z in self._unpack_data(msg): + yield z - def request_full_domain_data(self): + def _request_full_domain_data(self): request_msg = ('SEND_ALL_DATA', self.driver.address) - self.send_to_all_peers(request_msg) + self._send_to_all_peers(request_msg) def retrieve_all_data_objects(self): return self.data_objects.values() diff --git a/simulator/simple_dds/domain_participant.py b/simulator/simple_dds/domain_participant.py index f3ef23d..6bde917 100644 --- a/simulator/simple_dds/domain_participant.py +++ b/simulator/simple_dds/domain_participant.py @@ -40,9 +40,9 @@ def create_publisher(self, topic): def delete_publisher(self, publisher): pass - def create_subscriber(self, topic): + def create_subscriber(self, topic, listener=None): data = self.service.retrieve_filtered_data_objects(topic.get_name()) - new_subscriber = subscriber.Subscriber(self, topic, data) + new_subscriber = subscriber.Subscriber(self, topic, data, listener) self.service.assign_handle(new_subscriber) handle = new_subscriber.get_instance_handle() self.subscribers[handle] = new_subscriber @@ -55,9 +55,6 @@ def get_discovered_participants(self): pass # Usar o service - def update_subscriber(self, subscriber, data_object): - subscriber.receive_data(data_object) - def update_all_subscribers(self, data_object): for subscriber in self.subscribers.values(): subscriber.receive_data(data_object) \ No newline at end of file diff --git a/simulator/simple_dds/entity.py b/simulator/simple_dds/entity.py index ac01822..a9dced0 100644 --- a/simulator/simple_dds/entity.py +++ b/simulator/simple_dds/entity.py @@ -2,7 +2,7 @@ class Entity: def __init__(self): self.instance_handle = 0 - + def set_instance_handle(self, handle): if self.instance_handle != 0: raise RuntimeError("DDS Instance already has a handle.") diff --git a/simulator/simple_dds/subscriber.py b/simulator/simple_dds/subscriber.py index 16444d1..6244438 100644 --- a/simulator/simple_dds/subscriber.py +++ b/simulator/simple_dds/subscriber.py @@ -5,14 +5,15 @@ # TODO: Adicionar suporte para chamada de listener (callback). class Subscriber(entity.Entity): - - def __init__(self, participant, topic, data_objects): + + def __init__(self, participant, topic, data_objects, listener_method=None): super(Subscriber, self).__init__() self.participant = participant self.topic = topic self.available_data = Queue() for element in data_objects: self.available_data.put(element) + self.listener = listener_method def get_topic_name(self): return self.topic.get_name() @@ -20,6 +21,8 @@ def get_topic_name(self): def receive_data(self, data_object): if data_object.get_topic_name() == self.topic.get_name(): self.available_data.put(data_object) + if self.listener != None: + self.listener(self) def read(self): try: diff --git a/tests/simulator/__init__.py b/tests/simulator/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/tests/simulator/test_dds.py b/tests/simulator/test_dds.py new file mode 100644 index 0000000..66805d0 --- /dev/null +++ b/tests/simulator/test_dds.py @@ -0,0 +1,92 @@ +import pytest +import pprint +import sys, os +myPath = os.path.dirname(os.path.abspath(__file__)) +sys.path.insert(0, myPath + '/../../simulator/') +pprint.pprint(sys.path) + +import simpy +import random +from network import Network +from processor import Processor +from driver import Driver +from peer import Peer +from simple_dds import * + +@pytest.fixture +def environment_and_network(): + network_latency = 2 + max_peers = 100 + env = simpy.Environment() + net = Network(env, network_latency, max_peers) + return (env, net) + +@pytest.fixture +def subscriber_number(): + # Set Number: + return 5 + +def test_simple_publication_two_peers(environment_and_network): + env, net = environment_and_network + proc_latency = 3 + random.seed() + message = random.randrange(1000) + topic_name = random.randrange(1000) + wait_before_publication = 100 + wait_before_reading = 150 + simulation_time = 400 + container = None + + publishing_peer = initialize_peer(env, net, 0, 0, proc_latency) + subscribing_peer = initialize_peer(env, net, 1, 1, proc_latency) + publication = publishing_peer.wait_then_publish_message + reading = subscribing_peer.wait_then_read_message + add_process_to_simulation(env, publication(topic_name, message, wait_before_publication)) + add_process_to_simulation(env, reading(topic_name, message, wait_before_reading)) + env.run(until=simulation_time) + container = str(subscribing_peer.latest_read_msg) + assert container == str(message) + +def test_simple_publication_to_multiple_peers(environment_and_network, subscriber_number): + env, net = environment_and_network + proc_latency = 3 + random.seed() + message = random.randrange(1000) + topic_name = random.randrange(1000) + wait_before_publication = 100 + wait_before_reading = 150 + simulation_time = 1000 + subscriber_id = 1 + subscribers = [] + received_msg = None + + publishing_peer = initialize_peer(env, net, 0, 0, proc_latency) + publication = publishing_peer.wait_then_publish_message(topic_name, message, wait_before_publication) + add_process_to_simulation(env, publication) + for i in range(subscriber_number): + subscriber = initialize_peer(env, net, 0, i, proc_latency) + reading = set_up_subscription(subscriber, topic_name, message, wait_before_reading) + add_process_to_simulation(env, reading) + subscribers.append(subscriber) + + env.run(until=simulation_time) + for subscriber in subscribers: + received_msg = str(subscriber.latest_read_msg) + assert received_msg == str(message) + +def initialize_peer(environment, network, proc_id, peer_id, proc_latency): + proc = Processor(environment, proc_id, proc_latency) + dri = Driver(network, proc) + peer = Peer(dri, peer_id) + environment.process(dri.run()) + return peer + +def add_process_to_simulation(environment, method): + environment.process(method) + +def set_up_subscription(peer, topic_name, message, wait_time=100): + yield peer.driver.env.timeout(wait_time) + the_service = dds_service.DDS_Service(peer.driver) + participant = domain_participant.Domain_Participant(the_service) + topic = participant.create_topic(topic_name) + sub = participant.create_subscriber(topic, peer.read_new_message) \ No newline at end of file diff --git a/tests/simulator/test_initial_connection.py b/tests/simulator/test_initial_connection.py index 2206441..6311640 100644 --- a/tests/simulator/test_initial_connection.py +++ b/tests/simulator/test_initial_connection.py @@ -1,10 +1,14 @@ import pytest import simpy +import sys, os +myPath = os.path.dirname(os.path.abspath(__file__)) +sys.path.insert(0, myPath + '/../../simulator/') +pprint.pprint(sys.path) -from simulator.network import Network -from simulator.processor import Processor -from simulator.driver import Driver -from simulator.peer import Peer +from network import Network +from processor import Processor +from driver import Driver +from peer import Peer def test_connection(): @@ -40,4 +44,4 @@ def test_timeout_keep_alive(): env.run(until=50) - assert dri.address == None + assert dri.address == None \ No newline at end of file From 2c0b75bb86262de885f8cedb3a0c3aa7fb6b908d Mon Sep 17 00:00:00 2001 From: MA Date: Tue, 2 Jun 2020 15:33:02 -0300 Subject: [PATCH 10/12] Removed pprint calls. --- simulator/simple_dds/dds_service.py | 3 ++- tests/simulator/test_dds.py | 19 +++++++++---------- tests/simulator/test_initial_connection.py | 1 - 3 files changed, 11 insertions(+), 12 deletions(-) diff --git a/simulator/simple_dds/dds_service.py b/simulator/simple_dds/dds_service.py index 9ef627c..1811909 100644 --- a/simulator/simple_dds/dds_service.py +++ b/simulator/simple_dds/dds_service.py @@ -187,7 +187,8 @@ def _interpret_data(self, data): # Presumimos que os dados estejam em uma 2-tupla, sendo o primeiro elemento.. # .. uma string descrevendo o pedido, o segundo elemento os dados em si if data[0] not in self.message_handlers: - logging.warning(str(self.driver.get_time()) + ' :: ' + f'DDS Service (Handle {str(self.instance_handle)}): Invalid request: {str(data[1])}') + pass + #logging.warning(str(self.driver.get_time()) + ' :: ' + f'DDS Service (Handle {str(self.instance_handle)}): Invalid request: {str(data[1])}') else: self.message_handlers[data[0]](data[1]) diff --git a/tests/simulator/test_dds.py b/tests/simulator/test_dds.py index 66805d0..1075bde 100644 --- a/tests/simulator/test_dds.py +++ b/tests/simulator/test_dds.py @@ -1,9 +1,7 @@ import pytest -import pprint import sys, os myPath = os.path.dirname(os.path.abspath(__file__)) sys.path.insert(0, myPath + '/../../simulator/') -pprint.pprint(sys.path) import simpy import random @@ -16,7 +14,7 @@ @pytest.fixture def environment_and_network(): network_latency = 2 - max_peers = 100 + max_peers = 300 env = simpy.Environment() net = Network(env, network_latency, max_peers) return (env, net) @@ -24,7 +22,7 @@ def environment_and_network(): @pytest.fixture def subscriber_number(): # Set Number: - return 5 + return 150 def test_simple_publication_two_peers(environment_and_network): env, net = environment_and_network @@ -34,7 +32,7 @@ def test_simple_publication_two_peers(environment_and_network): topic_name = random.randrange(1000) wait_before_publication = 100 wait_before_reading = 150 - simulation_time = 400 + simulation_time = 300 container = None publishing_peer = initialize_peer(env, net, 0, 0, proc_latency) @@ -51,11 +49,11 @@ def test_simple_publication_to_multiple_peers(environment_and_network, subscribe env, net = environment_and_network proc_latency = 3 random.seed() - message = random.randrange(1000) - topic_name = random.randrange(1000) + message = 'test message' + topic_name = 'test topic' wait_before_publication = 100 - wait_before_reading = 150 - simulation_time = 1000 + wait_before_reading = 1000 + simulation_time = 900000 subscriber_id = 1 subscribers = [] received_msg = None @@ -70,7 +68,8 @@ def test_simple_publication_to_multiple_peers(environment_and_network, subscribe subscribers.append(subscriber) env.run(until=simulation_time) - for subscriber in subscribers: + for i, subscriber in enumerate(subscribers): + print(i) received_msg = str(subscriber.latest_read_msg) assert received_msg == str(message) diff --git a/tests/simulator/test_initial_connection.py b/tests/simulator/test_initial_connection.py index 6311640..29aad0c 100644 --- a/tests/simulator/test_initial_connection.py +++ b/tests/simulator/test_initial_connection.py @@ -3,7 +3,6 @@ import sys, os myPath = os.path.dirname(os.path.abspath(__file__)) sys.path.insert(0, myPath + '/../../simulator/') -pprint.pprint(sys.path) from network import Network from processor import Processor From b17e1a405aa4055e8dbf8a8a9e604ae6abd5244b Mon Sep 17 00:00:00 2001 From: MA Date: Wed, 29 Jul 2020 19:08:10 -0300 Subject: [PATCH 11/12] Relocated code from peer module to test module --- simulator/driver.py | 2 + simulator/peer.py | 31 ---------------- simulator/simple_dds/data_object.py | 5 ++- simulator/simple_dds/dds_service.py | 43 ++++++++-------------- simulator/simple_dds/domain_participant.py | 1 + simulator/simple_dds/publisher.py | 6 ++- simulator/simple_dds/subscriber.py | 5 +-- simulator/simple_dds/topic.py | 10 ++++- 8 files changed, 37 insertions(+), 66 deletions(-) diff --git a/simulator/driver.py b/simulator/driver.py index aa75963..e949a1c 100644 --- a/simulator/driver.py +++ b/simulator/driver.py @@ -84,6 +84,8 @@ def send_keepalive(self): def get_time(self): return self.env.now + # Coloca uma função na lista de processamento, que será executada + # em ordem. def async_function_call(self, call_info): self.async_calls.put(call_info) diff --git a/simulator/peer.py b/simulator/peer.py index 4496529..c1257e0 100644 --- a/simulator/peer.py +++ b/simulator/peer.py @@ -43,37 +43,6 @@ def on_advertise (self, msg): for z in self.driver.advertise(msg): yield z - # TODO: O nome não é adequado: faz mais do que publicar mensagem, antes cria objetos.. - # .. necessários. É preciso mudar depois. - def wait_then_publish_message(self, topic_name, message, wait_time=100): - yield self.driver.env.timeout(wait_time) - the_service = dds_service.DDS_Service(self.driver) - participant = domain_participant.Domain_Participant(the_service) - topic = participant.create_topic(topic_name) - pub = participant.create_publisher(topic) - pub.write(message) - - def wait_then_read_message(self, topic_name, message, wait_time=100): - yield self.driver.env.timeout(wait_time) - the_service = dds_service.DDS_Service(self.driver) - participant = domain_participant.Domain_Participant(the_service) - topic = participant.create_topic(topic_name) - sub = participant.create_subscriber(topic) - # Atenção à linha a seguir. Talvez seja necessário alterar o valor mais tarde. - yield self.driver.env.timeout(17) # Tempo para recebimento de mensagens de outros peers contendo dados do domínio. - self.latest_read_msg = sub.read() - - def dds_read_test (self): - yield self.driver.env.timeout(150) - print("read test") - the_service = dds_service.DDS_Service(self.driver) - participant = domain_participant.Domain_Participant(the_service) - n_topic = participant.create_topic("TEST") - sub = participant.create_subscriber(n_topic) - yield self.driver.env.timeout(17) - stuff = sub.read() - print(str(self.driver.env.now) + ':: ' + str(stuff)) - def read_new_message(self, subscriber): self.latest_read_msg = subscriber.read() diff --git a/simulator/simple_dds/data_object.py b/simulator/simple_dds/data_object.py index e7970e4..b7e5d7d 100644 --- a/simulator/simple_dds/data_object.py +++ b/simulator/simple_dds/data_object.py @@ -2,11 +2,12 @@ class Data_Object(entity.Entity): - def __init__(self, publisher_handle, topic, data): + def __init__(self, publisher, topic, data): super(Data_Object, self).__init__() - self.publisher_handle = publisher_handle + self.publisher = publisher self.topic = topic self.content = data + self.creation_time = self.publisher.participant.service.driver.get_time() def __str__(self): return str(self.content) diff --git a/simulator/simple_dds/dds_service.py b/simulator/simple_dds/dds_service.py index 1811909..037f54e 100644 --- a/simulator/simple_dds/dds_service.py +++ b/simulator/simple_dds/dds_service.py @@ -1,9 +1,12 @@ +# TODO +# - Não pedir todos os objetos-dado na criação do DDS Service; Fazer isso na.. +# .. criação de subscriber, e somente dados do tópico específico. + import logging from threading import Lock from singleton import Singleton from simple_dds import entity -# Possíveis problemas de uso mútuo nesta classe? Veremos quando testarmos. class UniqueHandleController(metaclass=Singleton): def __init__(self): @@ -17,28 +20,6 @@ def generate_handle(self): self.next_available_handle += 1 return handle -# Classe criada para impedir conflito entre leitura e escrita. -# Poderia fazer a lógica usando um Resource do simpy, mas prefiro evitar o uso.. -# ... do simpy na lógica do DDS. -class Data_Buffer: - - def __init__(self): - self.data_buffer = [] - self.lock = Lock() - - def write_to(self, data): - with self.lock: - self.data_buffer.append(data) - - def read_from(self, container): - with self.lock: - container = copy(self.data_buffer) - self.data_buffer = [] - - def is_empty(self): - return len(self.data_buffer) == 0 - -# Talvez seja necessário fazer dictionaries personalizados, que evitem problemas de acesso mútuo. class DDS_Service(entity.Entity): def __init__(self, driver): @@ -64,14 +45,13 @@ def set_instance_handle(self, handle): def get_instance_handle(self): return self.instance_handle - def _send_to_all_peers(self, msg): - self.driver.async_function_call(['advertise', msg]) - def _send_local_modification(self, type_name, data): change = (type_name, data) self._send_to_all_peers(change) + + def _send_to_all_peers(self, msg): + self.driver.async_function_call(['advertise', msg]) - # Fazer verificação de handle duplicada def assign_handle(self, entity): handle = self.handle_controller.generate_handle() entity.set_instance_handle(handle) @@ -97,6 +77,9 @@ def add_data_object(self, data_object): self._attach_data_object_to_topic(data_object) self._send_local_modification('NEW_DATA', data_object) + def announce_new_publisher(self, new_publisher): + self._send_local_modification('NEW_PUBLISHER', new_publisher) + def topic_exists(self, topic_name): return topic_name in self.topics @@ -149,6 +132,12 @@ def _append_data_object(self, new_data): self._send_data_object_to_all_participants(new_data) self._attach_data_object_to_topic(new_data) + # TODO: Ainda falta completar. + def _notify_subscribers_of_new_publisher(self, new_publisher): + for subscriber in self.participants.subscribers.values(): + topic_name = new_publisher.get_topic().get_name() + pass + def _send_data_object_to_all_participants(self, data_object): for participant in self.local_participants.values(): participant.update_all_subscribers(data_object) diff --git a/simulator/simple_dds/domain_participant.py b/simulator/simple_dds/domain_participant.py index 6bde917..887a085 100644 --- a/simulator/simple_dds/domain_participant.py +++ b/simulator/simple_dds/domain_participant.py @@ -35,6 +35,7 @@ def create_publisher(self, topic): self.service.assign_handle(new_publisher) handle = new_publisher.get_instance_handle() self.publishers[handle] = new_publisher + self.service.announce_new_publisher(new_publisher) return new_publisher def delete_publisher(self, publisher): diff --git a/simulator/simple_dds/publisher.py b/simulator/simple_dds/publisher.py index 62333a8..13997f3 100644 --- a/simulator/simple_dds/publisher.py +++ b/simulator/simple_dds/publisher.py @@ -8,7 +8,9 @@ def __init__(self, participant, topic): self.participant = participant self.topic = topic + def get_topic(self): + return self.topic + def write(self, data): - pub_handle = self.get_instance_handle() - new_data = data_object.Data_Object(pub_handle, self.topic, data) + new_data = data_object.Data_Object(self, self.topic, data) self.participant.service.add_data_object(new_data) \ No newline at end of file diff --git a/simulator/simple_dds/subscriber.py b/simulator/simple_dds/subscriber.py index 6244438..3051d01 100644 --- a/simulator/simple_dds/subscriber.py +++ b/simulator/simple_dds/subscriber.py @@ -3,7 +3,6 @@ from simple_dds import entity -# TODO: Adicionar suporte para chamada de listener (callback). class Subscriber(entity.Entity): def __init__(self, participant, topic, data_objects, listener_method=None): @@ -15,8 +14,8 @@ def __init__(self, participant, topic, data_objects, listener_method=None): self.available_data.put(element) self.listener = listener_method - def get_topic_name(self): - return self.topic.get_name() + def get_topic(self): + return self.topic def receive_data(self, data_object): if data_object.get_topic_name() == self.topic.get_name(): diff --git a/simulator/simple_dds/topic.py b/simulator/simple_dds/topic.py index c9da288..f9868b8 100644 --- a/simulator/simple_dds/topic.py +++ b/simulator/simple_dds/topic.py @@ -9,6 +9,8 @@ def __init__(self, topic_name, participant): self.publishers = [] self.subscribers = [] self.data_objects = {} + self.creation_time = self.participant.service.driver.get_time() + self.last_modified = self.creation_time def get_name(self): return self.name @@ -16,4 +18,10 @@ def get_name(self): def attach_data_object(self, data_object): if data_object.get_topic_name() == self.name: handle = data_object.get_instance_handle() - self.data_objects[handle] = data_object \ No newline at end of file + self.data_objects[handle] = data_object + self.last_modified = self.participant.service.driver.get_time() + + def can_be_deleted(self): + no_pubs = len(self.publishers) == 0 + no_subs = len(self.subscribers) == 0 + return no_pubs and no_subs \ No newline at end of file From 087174ba1192b9992ba14caffd60de4ab8e8f77b Mon Sep 17 00:00:00 2001 From: MA Date: Wed, 29 Jul 2020 19:14:27 -0300 Subject: [PATCH 12/12] Small fixes --- tests/simulator/test_dds.py | 35 ++++++++++++++++++++++++++++------- 1 file changed, 28 insertions(+), 7 deletions(-) diff --git a/tests/simulator/test_dds.py b/tests/simulator/test_dds.py index 1075bde..9b14a4d 100644 --- a/tests/simulator/test_dds.py +++ b/tests/simulator/test_dds.py @@ -22,7 +22,7 @@ def environment_and_network(): @pytest.fixture def subscriber_number(): # Set Number: - return 150 + return 15 def test_simple_publication_two_peers(environment_and_network): env, net = environment_and_network @@ -37,10 +37,10 @@ def test_simple_publication_two_peers(environment_and_network): publishing_peer = initialize_peer(env, net, 0, 0, proc_latency) subscribing_peer = initialize_peer(env, net, 1, 1, proc_latency) - publication = publishing_peer.wait_then_publish_message - reading = subscribing_peer.wait_then_read_message - add_process_to_simulation(env, publication(topic_name, message, wait_before_publication)) - add_process_to_simulation(env, reading(topic_name, message, wait_before_reading)) + publication = wait_then_publish_message(publishing_peer, topic_name, message, wait_before_publication) + reading = wait_then_read_message(subscribing_peer, topic_name, message, wait_before_reading) + add_process_to_simulation(env, publication) + add_process_to_simulation(env, reading) env.run(until=simulation_time) container = str(subscribing_peer.latest_read_msg) assert container == str(message) @@ -59,7 +59,7 @@ def test_simple_publication_to_multiple_peers(environment_and_network, subscribe received_msg = None publishing_peer = initialize_peer(env, net, 0, 0, proc_latency) - publication = publishing_peer.wait_then_publish_message(topic_name, message, wait_before_publication) + publication = wait_then_publish_message(publishing_peer, topic_name, message, wait_before_publication) add_process_to_simulation(env, publication) for i in range(subscriber_number): subscriber = initialize_peer(env, net, 0, i, proc_latency) @@ -88,4 +88,25 @@ def set_up_subscription(peer, topic_name, message, wait_time=100): the_service = dds_service.DDS_Service(peer.driver) participant = domain_participant.Domain_Participant(the_service) topic = participant.create_topic(topic_name) - sub = participant.create_subscriber(topic, peer.read_new_message) \ No newline at end of file + # read_new_message é o método 'listener' + sub = participant.create_subscriber(topic, peer.read_new_message) + +# TODO: O nome não é adequado: faz mais do que publicar mensagem, antes cria objetos.. +# .. necessários. É preciso mudar depois. +def wait_then_publish_message(peer, topic_name, message, wait_time=100): + yield peer.driver.env.timeout(wait_time) + the_service = dds_service.DDS_Service(peer.driver) + participant = domain_participant.Domain_Participant(the_service) + topic = participant.create_topic(topic_name) + pub = participant.create_publisher(topic) + pub.write(message) + +def wait_then_read_message(peer, topic_name, message, wait_time=100): + yield peer.driver.env.timeout(wait_time) + the_service = dds_service.DDS_Service(peer.driver) + participant = domain_participant.Domain_Participant(the_service) + topic = participant.create_topic(topic_name) + sub = participant.create_subscriber(topic) + # Atenção à linha a seguir. Talvez seja necessário alterar o valor mais tarde. + yield peer.driver.env.timeout(17) # Tempo para recebimento de mensagens de outros peers contendo dados do domínio. + peer.latest_read_msg = sub.read() \ No newline at end of file