Transports
This guide explains how transports connect FastCS controllers to external protocols, and how they use attribute callbacks to keep the protocol layer synchronized with attribute values.
Transport Architecture
A transport connects a ControllerAPI to an external protocol. The ControllerAPI provides read-only access to:
Attributes (AttrR , AttrW , AttrRW )
Command methods (@command )
Scan methods (@scan )
Sub-controller APIs (hierarchical structure)
Implementing a Transport
Subclass Transport and implement connect() and serve() :
import asyncio
from dataclasses import dataclass , field
from typing import Any
from fastcs.controllers import ControllerAPI
from fastcs.transports.transport import Transport
@dataclass
class MyTransport ( Transport ):
"""Custom transport implementation."""
host : str = "localhost"
port : int = 9000
def connect (
self ,
controller_api : ControllerAPI ,
loop : asyncio . AbstractEventLoop ,
) -> None :
"""Called during FastCS initialization.
Store the controller_api and set up your protocol server.
"""
self . _controller_api = controller_api
self . _loop = loop
self . _server = MyProtocolServer ( controller_api , self . host , self . port )
async def serve ( self ) -> None :
"""Called to start serving.
This runs as an async background task. It can block forever.
"""
await self . _server . start ()
@property
def context ( self ) -> dict [ str , Any ]:
"""Optional: Add variables to the interactive shell."""
return { "my_server" : self . _server }
Working with ControllerAPI
The ControllerAPI provides access to the controller’s attributes and methods. Use walk_api() to traverse the entire controller hierarchy and register all attributes and commands. Use pattern matching to handle different attribute types.
for controller_api in root_controller_api . walk_api ():
for name , attribute in controller_api . attributes . items ():
match attribute :
case AttrRW ():
protocol . create_read ( name , attribute )
protocol . create_write ( name , attribute )
case AttrR ():
protocol . create_read ( name , attribute )
case AttrW ():
protocol . create_write ( name , attribute )
for name , command in controller_api . command_methods . items ():
protocol . create_command ( name , command )
Attributes
Transports use attribute callbacks to keep their protocol-specific representations synchronized with attribute values:
eyJ2ZXJzaW9uIjoiMSIsImVuY29kaW5nIjoiYnN0cmluZyIsImNvbXByZXNzZWQiOnRydWUsImVuY29kZWQiOiJ4nO1dWXfaSlx1MDAxMn7Pr8jxvF40vS/z5oXYON6JbZyZOT4yXGJb7Fx1MDAwMeFtTv77VMtcdNpaXHUwMDAyXGbOJTGc3CXaaLrrq/pq6dL/Pnz8uFx1MDAxMTxccryNf33c8Fx1MDAxZetux29cZt2Hjb/M8XtvOPL7PThFwr+P+uNhPbzyLlxiXHUwMDA2o3/985/uYOBEdzn1fvflTq/jdb1eMIJr/1xyf//48X/hv+GM3zD3VzrqrvXYVPT6VnpcdTAwMGacXFxI+cVcdTAwMGZvXHIv+jkgdzjsP0SHXHUwMDFm4Vx1MDAxOJdcbjtcXDEuJeVMcz45+1x1MDAwNGcxUsRhjEmCkURS6cnZXHUwMDA3v1x1MDAxMdzBXHUwMDE1aHLkzvNv74LwJuFcdTAwMTDBMZaYXG5OtVx1MDAxMpNr3N5tx0vcNVxuhv22t93v9IdmhP/AWuI6icZ449bbt8P+uNeYXFxcdTAwMTNcZt3eaOBcdTAwMGVhQqLrmn6nU1xynsKnw8TCXHUwMDA0bqS+4/LHiEnqeN5d8KW3dz1vZCZcdTAwMWRPjvZcdTAwMDdu3Vx1MDAwZl7mJvpcdTAwMTVmhINKI1xcn/9GY1x1MDAxYbpdr2JcdTAwMTaoN+50Jof9XsMzU79xI3qJr+s1fnzdz9WNlo78OPI9XHUwMDFhvOeZJ2OuXHUwMDEwkZRhNTlcdTAwMTOJXHUwMDE5pipz+KjfXHUwMDBiZY4rTqlEKlpwf7RcdTAwMDNyXHUwMDE2hI9tup2RXHUwMDE3LYJcdTAwMTlbOSaD0W9cdTAwMWNcdTAwMGZcdTAwMWHuyy1cdTAwMTgkSHCOtEIsmpmO32unJ6DTr7ct3zLo+3FcdTAwMTE3n+j/PkYyXHUwMDEz/mXy///9a/rVWYmMbv+QesxGx1x1MDAxZFx1MDAwNdv9btdcdTAwMGbgd52YMaXHP1xu3GGwXHUwMDA1y+j3btPnvF4j50x416ZB4J3nZoRcdTAwMDLui59LQ9Xr3PRcdTAwMWYsc9bt33uH/sswR5d+cPdjlX5e+CEmNCmdsbfz7XJcdTAwMDc3q/290e5o/OTuN4/b7Zl0hsBYOyD+jGBcdTAwMDKyXHUwMDE3XHUwMDEzvVx1MDAxMFx1MDAxN1iDRlx1MDAxMZJSxCTSXGaz2ZSGdlx1MDAxNMFcdTAwMDIhIZRQXHUwMDFjRXK5Vlx1MDAxYUmlMVhcXGkwQoRcdTAwMDQwRIOJKVxygmSe0qCCIUmJjuzAn6s0SlmRXFxrjY1gS5yc9Vx1MDAwZsp+6fCLOz45U9fnx9dZrTH06sFcdTAwMGJuk2xcdTAwMDNm0mGUKUm4wppGzMCAg1PuSKk1XGJcdTAwMDTXjIro7E/FXHUwMDAxkuJwJlxiXFzEMFY8XHUwMDEy1IlcItFcdTAwMTHspiuOJlJ1hN6L4lxiZlVcdTAwMWM0V3FcdTAwMDBcdTAwMWMoKFtcdTAwMWTp9EhxcFFANqRcdTAwMTRgfV9FNibDi1x1MDAwNjpcdTAwMTFH9+x2t76HXHUwMDFho0rteHuvdrp7vePubsRhPFx1MDAxMcnAe1xmNiYnvmdcdTAwMTC8uFIqgE3xOJNjTFwiRmnmMCB5mHBOONFJxFx1MDAwMJyKXHUwMDExo5XDsyAh81jX91x1MDAwNZKxXHUwMDFkJEml/Fx1MDAwM1xyWmFEXHUwMDE15jY0sJiwp9BAXHUwMDEwXHUwMDE4XHUwMDE1qrnEr4HDm5jRUPDg939cdFx1MDAxN7I/jC9jv1x1MDAxN1T955eBJ45+crt+J1x1MDAxNMPEczY7/q2ZgY06XGbZXHUwMDFibsSnIfDBuZ1cXND1XHUwMDFijbiJqMNDXb/nXHIrs9ia/tC/9Xtu50v+2N1x0D/zRi+jXHUwMDBmhmMvPjfe3oR8OoRcdTAwMTeA97R2cTBcdTAwMTQj/7ZcIj4/b5Hq4VPwMJ7D5knhIHBBNNNKMSyTLjZlXHUwMDFh/G+pueZcZlxiMyFZusypcjC45kyBXHUwMDBiXHUwMDBlrnqkZtdWbzqg7+dcdTAwMDC0JFx1MDAwMjNNSFxcZH/gWapcXDxjXCKxIOBcbi3buu13Kv6R37h7wGf1XHUwMDEy5Sffbr3BYFx1MDAwNa1b8TiLrFx1MDAxYsy1ozRcdTAwMDbzpsC6MZmEhlwi06DBwDhS+DBcbvRcXPFcdTAwMTgvWdu66dB4mFx1MDAwM1x1MDAxYYgjxiWiNlsnXG6woZCSXHUwMDE0cbk6LuNPW3c+ilx1MDAxYqe/z8xNMS9pM5dcdTAwMWP2cizcw9HOOOiRXHUwMDFh2Tw+2CqV77+djlx1MDAwZt05LFx1MDAxY9eORlxuUVx1MDAwNFx1MDAxY1/TXHUwMDA0iDHW4EZcdTAwMDM4kYJcdTAwMGKo1lx1MDAxMcYn9k1Ih2vDYsEzpJws6tW9r3DQ81x1MDAxYyBmXHUwMDE4yFxiplbCKlnm6Fx1MDAwNMRCgXNBNH1cdTAwMTVhLbRwu4/jy/rJjnSDs06j5FXklbe3ilx1MDAxNq5wnIVcdTAwMTZcdTAwMGUhh0omXGZ3XHUwMDAzU5WKlVx1MDAxMkqmgENcdEeYXHUwMDBmg2s55VhkwTGXhXtX4JBoXHUwMDFlcFCkJWfCglxyITOR0lx0NjDHXHUwMDA0zuvVc+b23GHjXHUwMDAxlnFcdTAwMTWM3Fx1MDAxNFx1MDAwYpM2ctmhL8fQ7XyqlfdcdTAwMWbvdL2hn/uH366OxLdcdTAwMWKZxbHX6fiDUcbMXHUwMDEx5FxiXHUwMDBlSKZcdTAwMTK8Nk2SjpxEXHUwMDA2yoyAplRcXIlcYlx1MDAwNT+RXGa3OoarSoZcYlx1MDAxMFZp46pIOkxJJpEgmEmA/Fx1MDAxY8gmTe0x9rcgu+GO7rxfXGZtPFx1MDAwN7Rh0WDWpVx1MDAxNdsyXHUwMDE2hkljW4LXh5BYumP3XHUwMDEwfK3dPDz1v3qfR335dVx1MDAwZvdu7k6XafZwXHUwMDFjXHUwMDFkrzd7RHS++qdlt/Rp/2TAdqv7p1+ah7PWXHUwMDE1UEdoRFx1MDAxOFxmXG70ajJuqYRJIEolwauA/8YyiJNcdTAwMTShXHUwMDAzwi80XHUwMDAzOok1xpjSLFrgXG74XG5cZo9cdTAwMTGUIVxy6vv3QMvfYFx1MDAwN7FaOGkopVx1MDAwNs2ms5ZwIyw0oLkgQkQpgjR7XHUwMDE1in6DpGGupJpPVkaj52WwPHdcdTAwMTIxtnw/SoRerG15OKhstyulk+ftnWs+8J6uusiN6ZeNbr9cdTAwMTGKot9cdTAwMWL5XHIvfqbpP0bfnfydJlx1MDAwMkBDsiOkXHUwMDE2RCR+p4NcdTAwMDSVXHUwMDA0K66pwkCjVOZ3Rlx1MDAxMvX7pDtZvXvub/X9c8E292vbrd1g7/BqxlwiXHSCXHUwMDFkLjVHnGrwXHUwMDAyXCJrbzCtNXJcdTAwMDBOXHUwMDA0XHUwMDFib1xmXHUwMDBilk3cIFx1MDAwN4E8KS4lx+BcdTAwMTljSS15XHUwMDFjI11SmOBcdTAwMTlWlFx1MDAxMFx1MDAxOYuQrTVgSlx1MDAwM+4urFx1MDAwMbGG9Vx1MDAwNPYnbPFhWO7cslx0XGbUj0tcdTAwMDJcdTAwMDD5M1VgqUBYw/NZOV2mXHUwMDE2zC+lyNOP94/3pd2j5qN7c9N6wjW5WW1cdTAwMWZ4S9CPnCqCOdMmwVx1MDAxN2XDzVx1MDAwN/QjXHUwMDEzzNTOXHUwMDEw+Fx1MDAwM+58gXpcXDUlqPavtset2zLrXHUwMDFk35ev79tHu0FwM6tcdTAwMTKkXHUwMDBlgd+NXHUwMDExpVx1MDAxOLEkXHUwMDBilFxmOzBPTCDCgSFEyI90IHhBgilFlVx1MDAwNHeLSFx1MDAxYT0gVjnGXHUwMDFjXHUwMDEwPkpcdTAwMDFjlJhM+VpcdTAwMDfm6cCLZZSOIcyB5thCiCD1ub6UxCD0wJNWJ1x1MDAwZrBsXHUwMDE1mCur4fmsmP5cblwi+EaKXHUwMDBlU0YxU1xmtFx1MDAxZFx1MDAwMqX2V3JcIoyjrShXXG70IMNIZ+vmfkcqWOyzXHUwMDE3hoAx6DlcZoRcdTAwMTmIsWCapeJGmjlYI6SQkVx1MDAxYc6ygSNpJEdcYiBcdTAwMTHMlNOiRUPA70vpXHUwMDExu86zXHUwMDE39HBBqFx1MDAwMpm1KDeZLVx1MDAwYvip25RcdTAwMGXDRG9cdTAwMTNcdTAwMDKeJ5RcdTAwMTNJ44846mZcdTAwMTBcZs8uVyFcdTAwMDA8JfKaXHUwMDBlXHUwMDAwp1x1MDAwN76c8G+xOpyW51x1MDAwNOtcdTAwMDY0llx1MDAwMo/XlIFcdTAwMDKM5OGFzWhHXHUwMDAz0SFcdTAwMDQoIObZrTJcdTAwMDTujsd/uVx1MDAwNcc49tA1kJNAplx1MDAwYpevXHUwMDEyQbRU1MpdeD68wWprKYhES49cdTAwMDNcdTAwMTeHXHUwMDE3XHUwMDEyMvli4CZnvv9V9Nxixv765za22pvN21Pi31x1MDAwZjtPtfrluHSx3V3BuHXxOFx1MDAwYmy1QFx1MDAxYTw1rSmRXHUwMDE4K52sR5KaOqxcYuFcXIV1XGZaMXBtgFx1MDAxYS1cXI70vlx1MDAwMM5mt9QwuUhgzK07WJjOL0diUlCuSSxcXL1SpnpcdTAwMTUs9Vx1MDAxNCNptdTLNtTFXHUwMDAx7Fx1MDAxOUpupYNcdTAwMDTXnFAg3yq5zSRmqDmiIENcdTAwMTZLjZOWem2o58ExX9hQS1Cw4FxmWe10tngpSteaMmtMxavAXWT3ivOgXHSRnMuejsbuVsDlqVx1MDAxN+xcctTl4KTl33/KKX+a67llv9mqXHUwMDBmXHUwMDAyvNuq+sPH6vjxM71dxfxy8e+fkl/mjqJCUi6Z0ihpqUHHO8ZRNlx1MDAxOVx1MDAxNlx1MDAxMFxui6m2bUFcdTAwMDXUY6Xg94GbLVxiU2uA51x1MDAwMXxcdHFEoSXMNJXWfevx7b9pkHPBiJSS/6G5lOTG9bRIRndngPpcdTAwMGLSJm+UVsZMKFx1MDAwNWtOiFx1MDAwMjgn0sphZolwYuqqXHUwMDE4J4xjnU2gr27mpFhcdTAwMGZcdTAwMTfFXGYlJ1x1MDAwZXghgiEsuGKZmOHEXHUwMDEzyaEwXFyvXZFcdTAwMDU0nJgjaEg5NTVO1qAhJ1x1MDAwNUXVoHiEpvpNOnAs7IqsRNBwilx1MDAxYmBzRZZcdTAwMWUzLKFWc2u0u99Ccr99u3vjV3WbzMpRpFx1MDAwMzZcdTAwMGWUm8KIsWTtN2PSMZv5QHKAqlx1MDAxMFx1MDAxZNtDXHUwMDEzq1x1MDAwMZGEaUHNXHUwMDA2KImZbWeE1I5cdTAwMTRcdTAwMTLUI1x1MDAwMkWJ1FxcrOVd7XaSenHSQlx1MDAxNEIm92mrI8XxnS8prMNcdTAwMWFrrP7U8o98MTWfjID+XHUwMDFhXHUwMDBls9K5xM98X5VbXHUwMDA3JXT69WFcdTAwMWNcZnnv6Vx1MDAxM5qtooJzYVpgaFxy861xfM9pWFdLicklMpOMovGas1x1MDAwMreHYelQuIUxIFx1MDAwYkzNVULmXHUwMDAx+aB41Vx1MDAxNEg03uUqkE9L8HpcdTAwMDRcdTAwMDdf1ZJKNFx1MDAwYiFyXHUwMDBiyGBdXHUwMDE08MDXXHUwMDE1T0yGl1xiXHUwMDE0WLlnTERcdTAwMDdn+1x1MDAwZv7d551n3azJbrM6blx1MDAxY11cdTAwMWPOXHUwMDEzKNBEXHUwMDEyXHUwMDFj2660XHUwMDEyXHUwMDBlVUbYV15cdTAwMTlcdTAwMDVDP1x1MDAxNeFM6aNcdTAwMDI1Y1/DWdxcdTAwMGbws1x1MDAxY1xuOp2bskaMklx1MDAwMVSMsHIoaHRCsVx1MDAxMlx1MDAxOPyyjJ5cdNtcdTAwMDaaLZ3aTDLBlrotNo/38b5cdTAwMTRNTqmqvWVcdTAwMDGlyCySzfsg+TtbwEhcdTAwMGJAXHUwMDAxfVx1MDAxM+cjMiGvcD6qgFx1MDAwMKvvXHUwMDEx61x1MDAxNvUrfI8pNjrteySHvVx1MDAxY9ej++S2rj1V6Vf3+d7NsHX/rexaXHUwMDFhj9hcbi9cdTAwMDG8jtHAWlxuXHUwMDE2X5CXXadMOUb7wSllgmmW7TdcdTAwMTa8XHUwMDEy6YBGJ6ZnXHUwMDAz55TLefamvS/87i1MXHUwMDE0zDZ6Q6VtjkZBny3QzDB+uHWJjbamXHUwMDExhZvDm1ZTadrd2nm+apVr+utndfXbXHUwMDEzhVJW3P9oqmBfxVx1MDAxOaiCIFx1MDAwMrRcdTAwMTij4P5cdTAwMTFTXHUwMDA3l1I1YJtcdTAwMWNcdTAwMTRufmJcdTAwMTLzbIk3hmmmZn+7ksj0Z1WW5kZrppCraSpzlEwgU7TGsNX3yNZRxJKqXHUwMDAyIED56jGF8/CRq8BcdTAwMTWmXHUwMDE46kxcdTAwMGKX1MCXw1x1MDAxNs5cdTAwMGaean6Dd+vtNjo/6F1dXdx/sextt7NcdTAwMDVuukxQkzxcdTAwMDeUJlx1MDAwM5XAXHUwMDBmXHUwMDFjjVx1MDAwNMOmSVx1MDAwNcJ8JrZANahPRoRSXHUwMDE0XHUwMDAzqDlbh1x1MDAxNfIwvL8wWyCm53KY6rHRhfxqKCVghZB6XHUwMDFktF/HXHUwMDE2+tfq6Kh34YsxOdkptVsn1eblxe/PXHUwMDE2suL+R7NcdTAwMDX7Ks7GXHUwMDE2mKOwKcxQStJUgSXn0lx1MDAwMaqFhJDENJq20Vx1MDAwNexcYr1cdTAwMGUsvFLVfJ6dLlx1MDAxOG1cdTAwMDJ0XHUwMDAxWSOV+TtdXHUwMDExJVx1MDAxYUlO32Sb10Js4WR80/FHd6tAXHUwMDE3pljqNF3IjHw5fFx1MDAwMZdLT+Vj7Fx1MDAxZVTdvYutg4Py/TBozZGEMPt8NdEyLiNh20bTzk1QYFx1MDAxMsqQfiGyTa2s4Vx1MDAwNWBcdTAwMTlcdTAwMWFcdTAwMWNcdKK10pjFZGyN4iSKXHUwMDBmXHUwMDE2z0NgjTjiwlqzIFh+L1x1MDAwZnDQqDKb2X9cdTAwMWRjaKPaUFx1MDAxZJZuyiebpFs5fEKH3p3+7Vx1MDAxOUNW3P9owmBfxFx1MDAxOVxiXHUwMDAz5zBRWFx1MDAxOVViRE+mXHSDduBcdTAwMWbTbFxizkuadU2AbkjwbCRcIsQ0XHUwMDFikuvwwjya5nB2vkA0zL+g1jdcdTAwMDPQXFxcdTAwMGaEc1g28UY7J6NvfVx1MDAxNV2w90D/xVRhipHOUoWldz+vyC/jJ9roXHUwMDBlvlX2g/r95v1D7aA6x1ZcZlx1MDAwM1HKXHUwMDA0XHUwMDEzXHUwMDE4XHUwMDAwKJKZRODxjqaMXHUwMDEwTVx1MDAwMedUWXqfXHUwMDBik2o0b1x1MDAxM6KIcmnDr2mvXHUwMDBl56UgXHUwMDA0XFwvReeJNLyvbpjVWYlD/lx1MDAwYkBcdTAwMThcdTAwMDFkmeSEXHUwMDA15pxcdTAwMTdUbWNcdTAwMGVrxJdcdTAwMTlqeFx1MDAxMc/NreOzTmuzQZ9OR9vXtdaVbFeW2iN9SVtcdTAwMWSKx1nYPoBcdTAwMDLZ1piaPimK0yTZNjXAjnnPXHUwMDE2lkxQqUi2k1x1MDAxNFx1MDAxMVx1MDAwZTG7mShXUphcdTAwMTZcdTAwMTWLllx1MDAwMr8v0HyZ3Vx1MDAwNlJcdDxDXHUwMDEzbtvTwLKWcfI+XHUwMDEwjFx1MDAxOFx1MDAwMv23gptcdTAwMTIrx6tQXHUwMDA2PMVcdTAwMDSljWB80MuxgZeDXHUwMDFhXHUwMDFhdp9P9fbTqLLb0aSs44XGxVx1MDAxZJC4Y6JZlJnOsVwiWo1wvoDcUsRgmaSWVGfL+NdcdTAwMTXAy4Ty+TJcIu1cdTAwMTJcdTAwMTaLWDuoY0Jy02hcdTAwMTTojVx1MDAwMP/jT33dZmFcdHBpXVx1MDAwM/wxo1Pqg9Fu+fPtdfmucX2xR8jd871cdTAwMTYzXHUwMDExXHUwMDAymGVcdTAwMDdcdTAwMGKYcNM1SKLk1iAhkFx1MDAwM7ZcdTAwMDZmXHUwMDE5a9NZMqtTMOVcdTAwMGVcdTAwMTfF/YRiXCK1XG5bg1jq+N/aePpcImf3o73ztJacgN9i48sqV1uY1SOYrtLbeX8ygut+73owXHUwMDBlrsF8d8xC/6dcdTAwMTfuunFcdTAwMDY5/jKez19cdTAwMWWGXHUwMDAymM9cdTAwMTSC/iCPJiR+RZpcdTAwMTPMNOzlkIUj97l76DW3n9uDO6/j6vFgWLdU7VmBLVx1MDAxZESkUZCIcJzkXG7aXHUwMDE0JFFg8khQqpUlNzZcdTAwMDOuyVx1MDAxYde5uK7NXHUwMDAza1gkyZHVXHLOb4VtXlx1MDAwZqDUKnXCzkP1MqDc8Zq/XHUwMDAyycuG70OJPtzqq9HFqFxcq1x1MDAxZH456nzbrlx1MDAxZc1cdTAwMDRfiZBcdTAwMTPm/qnpTZPy07GJY1NJgT2ajZ+Wd5mB4camzVx1MDAxZlNKMPDmIzl/XHUwMDFkfN/eTV8p+F7NXHUwMDBlX3C1TLGh9V1mXHUwMDA1bcBcdTAwMDQhVFx1MDAxMqTfxE1fXGK+lWNnlFc2v1x1MDAxYajNjHA5YEVl7/G0/alSXHUwMDFknm6dsnp3c+v03PJcdTAwMTJ7SyFcbmbKXHUwMDExYGNcdTAwMDVcdTAwMDdrKkRcdTAwMWGtlDiUXHUwMDEw825cbqpcYlDtXGZapZq6w2WN1ny0fp2jXHUwMDEyXHUwMDA1oEq0sFx1MDAxNreR/EpcdTAwMTTAquk9q1bP2Fx1MDAwMlx1MDAxNsb5pasrg9f0XHUwMDE4l4PYqzN//1x1MDAwNPVLT/T8cYtcdTAwMWZtb1dZuzFcdTAwMWJizWtwlaZMU9NqPun2amyKVE37J81cdTAwMTBcIjRbc1x1MDAxMr6JXHUwMDAzT6k0X9PjXFzEurMjlmBcdTAwMDHAI9hcdTAwMTZcdTAwMDdX+VxyMVx1MDAxOJhlXHSOy+q9Se3lkSnn8Wz1UTzjuJeD7Fx1MDAwMOPjwZa89IPnqiBfWzV1WZ+JOKeRndxCXCLA0lx1MDAxMlNoxuFcdTAwMGZcdTAwMDJcdTAwMWGdRbbEjq1H39rZzUdzY1x1MDAxZTRTzqR5449cdTAwMDXOsULPTFNskyt+o30jXHUwMDBiO7spZIxWXHUwMDE4xMXDnVx1MDAxYrtcdTAwMWZ+XHUwMDA02zfcwaBcdTAwMWFcdTAwMTg98DNcdTAwMDOyce97XHUwMDBmW1m5/0cz/JjUeYh8I/NemDj5/uH7/1x1MDAwMXuzV+wifQ== Transport User Hardware AttrRW AttrR AttrW Send Update Publish Put IO _on_put_callback AttrW.put _on_put_callback IO.send IO.update update_callback AttrR.update _on_update_callbacks
The diagram above shows the data flow between users, transports, attributes, and
hardware. The following table gives an overview of the data flow for the transport
layer.
On Update Callbacks
Use add_on_update_callback() to update the protocol layer when an attribute’s value changes.
def create_read ( name , attribute ):
protocol_read = Protocol ( name )
async def update_protocol_value ( value ):
protocol_read . post ( value )
attribute . add_on_update_callback ( update_protocol_value )
The callback receives the new value and should update the protocol-specific
representation (e.g., posting to a PV, updating a REST endpoint cache, publishing the
change to a subscriber).
Update Datatype Callbacks
Use add_update_datatype_callback() to update protocol metadata when an attribute’s datatype changes. This is useful for protocols that expose datatype metadata (like EPICS record fields).
def create_read ( name , attribute ):
...
attribute . add_on_update_callback ( update_protocol_value )
def update_protocol_metadata ( datatype : DataType ):
protocol_read . set_units ( datatype . units )
protocol_read . set_limits ( datatype . min , datatype . max )
attribute . add_update_datatype_callback ( update_protocol_metadata )
The callback receives the new DataType instance and should update the protocol’s metadata representation (e.g., EPICS record fields like EGU , HOPR , LOPR ).
Put
When the transport receives a write request from the protocol, call await attribute.put(value) to forward it to the attribute. This triggers validation and
propagates the value to the device via the IO layer. The transport should also update
its own setpoint display directly rather than relying on the sync setpoint callback
being called.
def create_write ( name , attribute ):
protocol_setpoint = Protocol ( name )
async def handle_write ( value ):
protocol_setpoint . post ( value )
await attribute . put ( value )
Sync Setpoint Callbacks
Use add_sync_setpoint_callback() to update the protocol layer’s setpoint
representation when the transport receives a write request. This is called when
AttrW.put is called with sync_setpoint=True .
Each transport is responsible for updating its own setpoint display while actioning the
change and should not rely on its sync setpoint callback being called by the attribute,
nor should it call AttrW.put with sync_setpoint=True . Setpoints should not be synced
between transports in this case - this is intentional to show which transport the change
came from.
def create_write ( name , attribute ):
...
async def update_setpoint_display ( value ):
protocol_setpoint . post ( value )
attribute . add_sync_setpoint_callback ( update_setpoint_display )
Sync setpoint callbacks are used in specific cases:
When an attribute delegates to other attributes that actually communicate with the device
During the first update of an AttrRW , to initialize the setpoint with the first readback value
Commands
Transports can trigger commands, which connect directly to method calls rather than stateful attributes.
def create_command ( name , command ):
protocol_command = Protocol ( name )
async def handle_command ():
await command . fn ()
protocol_command . post ()
Usage
Transports are automatically registered when subclassing Transport :
from fastcs.transports import Transport
@dataclass
class MyTransport ( Transport ):
# Automatically added to Transport.subclasses
pass
This allows the transport to be used in YAML configuration files.