{ "info": { "author": "Suthep Pomjaksilp", "author_email": "sp@laz0r.de", "bugtrack_url": null, "classifiers": [ "Development Status :: 5 - Production/Stable", "Intended Audience :: Developers", "License :: OSI Approved :: MIT License", "Programming Language :: Python", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.6", "Programming Language :: Python :: Implementation :: CPython", "Programming Language :: Python :: Implementation :: PyPy", "Topic :: Communications" ], "description": "\n# EvoCount nexedge radio communication\nThis module provides high level data transmission via radio link using Kenwood Nexedge devices.\n\n## Prerequisites\nThe following things are needed.\n* git\n* Python >= 3.6\n* hardware serial device or RS232 dongle, the executing user has to have writing permission\n\n## Installation\n* git clone `git@github.com:evocount/nexedge.git`\n* `cd nexedge && pipenv install`\n\n## Transmitting and receiving data\nThe following sections will describe the what each component of this packages does.\n\n### Short primer on radio communication\nA radio transmission always consists of the transmitter or sender and the receiving unit.\nThe transmission itself _travels_ through a common radio channel.\nIf a sender/receiver pair is transmitting data, the channel is blocked.\nSince usual case consists of more than 2 transceivers, only one pair can be actively sending at one point of time.\n\nThe nexedge devices by Kenwood provide two functions to transmit a payload via air.\nShort-data-message (SDM) and long-data-messages (LDM) are handled by the devices.\nSDMs are displayed on the screen, LDMs are not.\n\nInterfacing the transceivers is possible via a serial interface at the back of the unit.\nThe RS232 is carried via a D-SUB25 connector.\nIn most cases a D-SUB9 to 25 serial modem cable is necessary since the device features a female connector.\nBy default the serial configuration is the following:\n```python\nimport serial\n\nbaudrate = 9600\nparity = PARITY_NONE\nstopbits = serial.STOPBITS_TWO\nbytesize = serial.EIGHTBITS\n```\n\nThe data which is available via serial is encapsulated in packages with a start and s stop byte:\n```python\n\\x02 SEQ DATA \\x03\n```\n`SEQ` is an identifier for the type of package (display message, status message, LDM, SDM).\n\nControlling of the device is possible in the same way by constructing a command:\n```python\n\\x02 gGU 00002 helloWorld \\x03\n```\nIn this case `b'gGU'` is the command for a LDM and `b'00002'` is the target transceiver.\nEvery command is followed by a ACK signal:\n```python\n\\x02 1 \\x03\n```\nor in the failing case:\n```python\n\\x02 0 \\x03\n```\nAs transmissions can take up to 40s the ACK can be delayed by quite some time.\n\n### Initializing `nexedge.RadioCommunicator`\nWhen using you should only ever use this class.\nThe communicator provides provides a high-level interface to send and receive data via radio.\n\nExample usage:\n```python\nloop = asyncio.get_event_loop()\n\ncom = RadioCommunicator(serial_kwargs=\n {\"url\": settings.RADIO_SERIAL_URL,\n \"baudrate\": settings.RADIO_SERIAL_BAUDRATE},\n listeners=[\"about-me\"],\n timeout=settings.RADIO_TIMEOUT)\n\n# start the handler for incoming data\nloop.create_task(com.data_handler())\n```\n\n### Sending a payload with `nexedge.RadioCommunicator.send()`\nImagine you (transceiver b\"00001\") want to send the payload\n```python\np = {\n \"name\": \"dog\",\n \"tail\": True,\n \"sound\": \"wuff\"\n}\n```\nto the target transceiver `b\"00002\"`.\n\nExample with `com` from the above section:\n```python\nresult = await com.send(target_id=b\"00002\",\n data=p,\n meta={})\n```\nFirst of all it is to note that `send` is a awaitable coroutine!\n\nThe return value of this command is either `True` or `False` as indicated by the ACK.\nIf no ACK at all is received during `timeout` => `ConfirmationTimeout` is raised.\n\nUnder the hood the data is placed into a dictionary:\n```python\n# add some meta data to our payload\ndata = {\n \"counter\": self._counter,\n \"meta\": meta,\n \"payload\": data,\n}\n```\nThe counter just tags the transmission and metadata can be added as a dictionary.\n\n### Receiving the payload with `nexedge.RadioCommunicator.get_target_queue()`\nThe `data_handler()` coroutine continuously places received data into the a so-called target queue.\nThis queue consists of tuples `(target_id, data)` of data which is received from the transceiver with a specific target id.\nNote that every transceiver has a unique queue!\n\nTo receive the data from `b\"00001\"` (above section), you have to acquire the queue:\n```python\nqueue = com.get_target_queue(target=`b\"00001\"`)\nremote_id, data = await queue.get()\nprint(data[\"payload\"])\n\n\"\"\"\n {\n \"name\": \"dog\",\n \"tail\": True,\n \"sound\": \"wuff\"\n }\n\"\"\"\n```\n`remote_id` will carry `b\"00002\"`.\n\n### Transmitting and receiving _broadcast_ data with triggers\nThe term broadcast has to be used with caution since the transmission still targets only one transceiver.\nBut in this case the target transceiver does not know beforehand from whom it will get data.\nA classical use case in the `pdm` scenario is the transmission of the slave configuration, aka. its `about-me` data.\n\nFirst we will observe the receiving side.\nTo retrieve such data, we have to listen for a `trigger`.\nDuring initialization of the communicator a list of listeners can be given `listeners=[\"about-me\"]`.\nThis sets up a separate `listener_queue` for this trigger.\nTo receive data the only thing we have to do is to get from this queue:\n```python\nqueue = com.get_listener_queue(\"about-me\")\nremote_id, data = await queue.get()\n```\n\nAs indicated beforehand the metadata of a transmission can be used to get transmit addition information.\nThe trigger is just a special metadata keyword:\n```python\ncom.send(receiver=b\"00002\",\n data=self.model.configuration,\n meta={\"trigger\": \"about-me\"})\n```\n\n## Caveats\n* the radio channel is a shared medium, even if the transmission is directed to a single transceiver, it still blocks the channel.\nAs a result the user has to make sure only one radio is talking at a time.\n* a considerable amount of time is spent to wait until the radio channel is considered free again. If the channel is not updated for 10s, it is considered free and the transmission starts.\n* `nexedge` does not do any retries of sending.\n* transmissions can take up to 40s when sending 4000 bytes. To counter this, every data is serialized with json, compressed with zlib and encoded in base64. With this method up to 220 log events can be transmitted in one package.\n\n## License\n\nThis project is licensed under the [MIT License](LICENSE.md).\n\n\n", "description_content_type": "text/markdown", "docs_url": null, "download_url": "", "downloads": { "last_day": -1, "last_month": -1, "last_week": -1 }, "home_page": "https://github.com/evocount/nexedge", "keywords": "", "license": "MIT", "maintainer": "", "maintainer_email": "", "name": "nexedge", "package_url": "https://pypi.org/project/nexedge/", "platform": "", "project_url": "https://pypi.org/project/nexedge/", "project_urls": { "Homepage": "https://github.com/evocount/nexedge" }, "release_url": "https://pypi.org/project/nexedge/1.0.0/", "requires_dist": [ "pyserial-asyncio", "pytest ; extra == 'test'", "pytest-cov ; extra == 'test'", "pytest-asyncio ; extra == 'test'", "pytest-mock ; extra == 'test'", "pytest-xdist ; extra == 'test'" ], "requires_python": ">=3.6.0", "summary": "Kenwood Nexedge communication", "version": "1.0.0" }, "last_serial": 5949720, "releases": { "1.0.0": [ { "comment_text": "", "digests": { "md5": "6115a156fe2b251595c0a9413b1ee30a", "sha256": "20abba73a17febda277ca84cf92532e9dc5b985721d625a7365550b12bb61be5" }, "downloads": -1, "filename": "nexedge-1.0.0-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "6115a156fe2b251595c0a9413b1ee30a", "packagetype": "bdist_wheel", "python_version": "py2.py3", "requires_python": ">=3.6.0", "size": 17895, "upload_time": "2019-10-09T13:11:47", "url": "https://files.pythonhosted.org/packages/c2/e2/fcdb2a8531db1a234e4b9f053c83c0400d588205635d9ee347db364a8857/nexedge-1.0.0-py2.py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "f6d6928933f9b3ebc97d19f39c33cfec", "sha256": "a0e154ea4c6ba9478478229ece287d08ab897b87e3ff2a32417c88163a2fed61" }, "downloads": -1, "filename": "nexedge-1.0.0.tar.gz", "has_sig": false, "md5_digest": "f6d6928933f9b3ebc97d19f39c33cfec", "packagetype": "sdist", "python_version": "source", "requires_python": ">=3.6.0", "size": 17152, "upload_time": "2019-10-09T13:11:50", "url": "https://files.pythonhosted.org/packages/3e/52/4670f421191085a8ae7b75a1d8d214eceb391631bf889f627d509831e76a/nexedge-1.0.0.tar.gz" } ] }, "urls": [ { "comment_text": "", "digests": { "md5": "6115a156fe2b251595c0a9413b1ee30a", "sha256": "20abba73a17febda277ca84cf92532e9dc5b985721d625a7365550b12bb61be5" }, "downloads": -1, "filename": "nexedge-1.0.0-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "6115a156fe2b251595c0a9413b1ee30a", "packagetype": "bdist_wheel", "python_version": "py2.py3", "requires_python": ">=3.6.0", "size": 17895, "upload_time": "2019-10-09T13:11:47", "url": "https://files.pythonhosted.org/packages/c2/e2/fcdb2a8531db1a234e4b9f053c83c0400d588205635d9ee347db364a8857/nexedge-1.0.0-py2.py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "f6d6928933f9b3ebc97d19f39c33cfec", "sha256": "a0e154ea4c6ba9478478229ece287d08ab897b87e3ff2a32417c88163a2fed61" }, "downloads": -1, "filename": "nexedge-1.0.0.tar.gz", "has_sig": false, "md5_digest": "f6d6928933f9b3ebc97d19f39c33cfec", "packagetype": "sdist", "python_version": "source", "requires_python": ">=3.6.0", "size": 17152, "upload_time": "2019-10-09T13:11:50", "url": "https://files.pythonhosted.org/packages/3e/52/4670f421191085a8ae7b75a1d8d214eceb391631bf889f627d509831e76a/nexedge-1.0.0.tar.gz" } ] }