phy.py 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155
  1. # -*- coding: utf-8 -*-
  2. #
  3. # PROFIBUS DP - Communication Processor PHY access library
  4. #
  5. # Copyright (c) 2013-2021 Michael Buesch <m@bues.ch>
  6. #
  7. # Licensed under the terms of the GNU General Public License version 2,
  8. # or (at your option) any later version.
  9. #
  10. from __future__ import division, absolute_import, print_function, unicode_literals
  11. from pyprofibus.compat import *
  12. import time
  13. import sys
  14. from collections import deque
  15. from pyprofibus.util import *
  16. __all__ = [
  17. "PhyError",
  18. "CpPhy",
  19. ]
  20. class PhyError(ProfibusError):
  21. """PHY exception.
  22. """
  23. class CpPhy(object):
  24. """PROFIBUS CP PHYsical layer base class.
  25. """
  26. PFX = "PHY: "
  27. # Profibus baud-rates
  28. BAUD_9600 = 9600
  29. BAUD_19200 = 19200
  30. BAUD_45450 = 45450
  31. BAUD_93750 = 93750
  32. BAUD_187500 = 187500
  33. BAUD_500000 = 500000
  34. BAUD_1500000 = 1500000
  35. BAUD_3000000 = 3000000
  36. BAUD_6000000 = 6000000
  37. BAUD_12000000 = 12000000
  38. __slots__ = (
  39. "debug",
  40. "__txQueueDAs",
  41. "__txQueueTelegrams",
  42. "__allocUntil",
  43. "__secPerFrame",
  44. )
  45. def __init__(self, debug=False, *args, **kwargs):
  46. self.debug = debug
  47. self.__close()
  48. def _debugMsg(self, msg):
  49. if self.debug:
  50. print(self.PFX + str(msg))
  51. def _warningMsg(self, msg):
  52. print("%sWarning: %s" % (self.PFX, str(msg)))
  53. def close(self):
  54. """Close the PHY device.
  55. This method may be reimplemented in the PHY driver.
  56. """
  57. self.__close()
  58. def __close(self):
  59. self.__txQueueDAs = deque()
  60. self.__txQueueTelegrams = [None] * (0x7F + 1)
  61. self.__allocUntil = monotonic_time()
  62. self.__secPerFrame = 0.0
  63. def sendData(self, telegramData, srd):
  64. """Send data to the physical line.
  65. Reimplement this method in the PHY driver.
  66. """
  67. raise NotImplementedError
  68. def pollData(self, timeout):
  69. """Poll received data from the physical line.
  70. timeout => timeout in seconds.
  71. 0.0 = no timeout, return immediately.
  72. negative = unlimited.
  73. Reimplement this method in the PHY driver.
  74. """
  75. raise NotImplementedError
  76. def poll(self, timeout=0.0):
  77. """timeout => timeout in seconds.
  78. 0.0 = no timeout, return immediately.
  79. negative = unlimited.
  80. """
  81. if self.__txQueueDAs:
  82. self.__send()
  83. return self.pollData(timeout)
  84. def __send(self):
  85. now = monotonic_time()
  86. if self.__canAllocateBus(now):
  87. da = self.__txQueueDAs.popleft()
  88. telegram, srd, maxReplyLen = self.__txQueueTelegrams[da]
  89. self.__txQueueTelegrams[da] = None
  90. telegramData = telegram.getRawData()
  91. self.__allocateBus(now, len(telegramData), maxReplyLen)
  92. self.sendData(telegramData, srd)
  93. def send(self, telegram, srd, maxReplyLen=-1):
  94. if maxReplyLen < 0 or maxReplyLen > 255:
  95. maxReplyLen = 255
  96. da = telegram.da
  97. if self.__txQueueTelegrams[da] is None:
  98. self.__txQueueDAs.append(da)
  99. self.__txQueueTelegrams[da] = (telegram, srd, maxReplyLen)
  100. self.__send()
  101. def setConfig(self, baudrate=BAUD_9600, *args, **kwargs):
  102. """Set the PHY configuration.
  103. This method may be reimplemented in the PHY driver.
  104. """
  105. symLen = 1.0 / baudrate
  106. self.__secPerFrame = symLen * float(1 + 8 + 1 + 1)
  107. def __canAllocateBus(self, now):
  108. return now >= self.__allocUntil
  109. def __allocateBus(self, now, nrSendOctets, nrReplyOctets):
  110. secPerFrame = self.__secPerFrame
  111. seconds = secPerFrame * nrSendOctets
  112. if nrReplyOctets:
  113. pass#TODO IFS
  114. seconds += secPerFrame * nrReplyOctets
  115. pass#TODO
  116. self.__allocUntil = now + seconds
  117. def releaseBus(self):
  118. self.__allocUntil = monotonic_time()
  119. if self.__txQueueDAs:
  120. self.__send()
  121. def clearTxQueueAddr(self, da):
  122. """Remove all TX queue entries for the given destination address.
  123. """
  124. if da in self.__txQueueDAs:
  125. self.__txQueueDAs.remove(da)
  126. self.__txQueueTelegrams[da] = None