### Perform Data Acquisition and Device Settings Source: https://github.com/fermi-ad/acsys-python/wiki/acsys.dpm_tutorial A complete example showing how to register devices for reading and settings, start acquisition, and process replies in an asynchronous loop. ```python #!/usr/bin/env python3 import logging import acsys.dpm from acsys.dpm import (ItemData, ItemStatus) async def my_app(con): async with acsys.dpm.DPMContext(con) as dpm: await dpm.enable_settings(role='testing') # Register devices: reading temperature and setting fan speed await dpm.add_entry(0, 'Z:RMTEMP@p,10s') await dpm.add_entry(1, 'Z:RMFAN.SETTING@N') await dpm.start() async for ii in dpm.replies(): if ii.is_reading_for(0): fan_duty = min(max(ii.data - 70, 0), 20) * 5.0 await dpm.apply_settings([(1, fan_duty)]) elif ii.is_status_for(1): if ii.status.isFatal: log.warning('cannot set fan speed: %s', ii.status) acsys.run_client(my_app) ``` -------------------------------- ### SYNC Service Client Example Source: https://github.com/fermi-ad/acsys-python/wiki/acsys.sync An example Python script demonstrating how to use the acsys.sync library to register for and receive clock and state events. ```APIDOC ## SYNC Service Client Example ### Description This script demonstrates how to use the `acsys.sync` library to register for specific event information (e.g., $02 and $8F) and display them as they arrive in real-time. ### Method Asynchronous client implementation using `asyncio`. ### Endpoint N/A (This is a client-side script) ### Parameters N/A ### Request Example ```python #!/usr/bin/env python3 import asyncio import logging import acsys.sync # Set up the logger. FORMAT = '%(asctime)-15s [%(levelname)s] %(message)s' logging.basicConfig(format=FORMAT) log = logging.getLogger('acsys') log.setLevel(logging.INFO) async def my_client(con): async for ii in acsys.sync.get_events(con, ['e,2', 'e,8f']): log.info('%s', str(ii)) acsys.run_client(my_client) ``` ### Response Events are logged to the console as they are received. #### Success Response (200) N/A #### Response Example ``` 2023-10-27 10:30:00,123 [INFO] ClockEvent(stamp=1698395400.123456, ev=2, number=1) 2023-10-27 10:30:01,456 [INFO] StateEvent(stamp=1698395401.456789, di=10, value=1) ``` ``` -------------------------------- ### Install ACSys Python Client Source: https://context7.com/fermi-ad/acsys-python/llms.txt Installs the ACSys Python client library. The basic installation uses a standard pip command, while the installation with settings support requires an additional package and Kerberos authentication. ```bash python3 -m pip install acsys --extra-index-url https://www-bd.fnal.gov/pip3 # With settings support (requires Kerberos authentication) python3 -m pip install "acsys[settings]" --extra-index-url https://www-bd.fnal.gov/pip3 ``` -------------------------------- ### Perform Data Acquisition with DPM Source: https://context7.com/fermi-ad/acsys-python/llms.txt A complete example of using the DPMContext to configure device entries, start acquisition, and process asynchronous replies. It demonstrates filtering by tag and handling status updates within the stream. ```python import logging import acsys import acsys.dpm FORMAT = '%(asctime)-15s [%(levelname)s] %(message)s' logging.basicConfig(format=FORMAT) log = logging.getLogger('acsys') log.setLevel(logging.INFO) async def my_app(con): log.info('Connected with handle: %s', con.handle) async with acsys.dpm.DPMContext(con) as dpm: # Configure devices await dpm.add_entry(0, 'M:OUTTMP@p,1000') # Outdoor temp at 1Hz await dpm.add_entry(1, 'Z:CUBE_X.SETTING@p,2000') # Cube X at 0.5Hz await dpm.start() log.info('Acquisition started') readings = 0 async for item in dpm.replies(tmo=10.0): if item.is_reading_for(0): log.info('Outdoor temp: %.2f %s', item.data, item.meta.get('units', '')) elif item.is_reading_for(1): log.info('Cube X: %.4f', item.data) elif item.is_status: log.warning('Error for tag %d: %s', item.tag, item.status) readings += 1 if readings >= 20: break log.info('Collected %d readings', readings) if __name__ == '__main__': acsys.run_client(my_app) ``` -------------------------------- ### Apply one-off device settings Source: https://github.com/fermi-ad/acsys-python/wiki/Example-DAQ-scripts Demonstrates how to apply a single setting to one or multiple devices without continuous acquisition. Uses the 'N' (never) event modifier to prevent data acquisition setup. ```python import acsys.dpm async def my_app(con): # Setup context async with acsys.dpm.DPMContext(con) as dpm: # Check kerberos credentials and enable settings await dpm.enable_settings(role='testing') # Add acquisition requests await dpm.add_entry(0, 'Z:CUBE_X.SETTING@N') # Set an arbitrary value await dpm.apply_settings([(0, 42)]) acsys.run_client(my_app) ``` ```python import acsys.dpm async def my_app(con): # Setup context async with acsys.dpm.DPMContext(con) as dpm: # Check kerberos credentials and enable settings await dpm.enable_settings(role='testing') # Add acquisition requests await dpm.add_entry(0, 'Z:CUBE_X.SETTING@N') await dpm.add_entry(1, 'Z:CUBE_Y.SETTING@N') await dpm.add_entry(2, 'Z:CUBE_Z.SETTING@N') # Set an arbitrary value await dpm.apply_settings([(0, 42), (1, 42), (2, 42)]) acsys.run_client(my_app) ``` -------------------------------- ### Control DPM Data Acquisition with Start and Stop Source: https://context7.com/fermi-ad/acsys-python/llms.txt Controls data acquisition for the configured device list. The `start()` method begins streaming data, while `stop()` pauses acquisition without clearing the list of configured devices. This allows for resuming data collection later. ```python import acsys.dpm async def my_app(con): async with acsys.dpm.DPMContext(con) as dpm: await dpm.add_entry(0, 'M:OUTTMP@p,1000') # Start data acquisition await dpm.start() print('Acquisition started') # Read some data... count = 0 async for item in dpm.replies(): print(f'Reading: {item.data}') count += 1 if count >= 5: break # Stop acquisition (list preserved) await dpm.stop() print('Acquisition stopped') acsys.run_client(my_app) ``` -------------------------------- ### Acquire and Process Live Data Stream in Python Source: https://github.com/fermi-ad/acsys-python/wiki/Example-DAQ-scripts This snippet demonstrates how to set up a context for live data acquisition using `acsys.dpm.DPMContext`. It shows how to add data entries, start the acquisition, and asynchronously iterate over incoming events, processing them based on their associated tags. This requires the `acsys` library. ```python import sys import acsys.dpm async def my_app(con): # Setup context async with acsys.dpm.DPMContext(con) as dpm: # Add acquisition requests await dpm.add_entry(0, 'Z:CUBE_X.SETTING') await dpm.add_entry(1, 'Z:CUBE_Y.SETTING') # Start acquisition await dpm.start() # Process incoming data async for evt_res in dpm: if evt_res.is_reading_for(0): # This 0 argument matches the tag in `add_entry` if evt_res.is_reading_for(0): print(evt_res) else: # This is responses to tags that aren't 0 pass else: # This is likely a status response pass acsys.run_client(my_app) ``` -------------------------------- ### DPM.start and DPM.stop Source: https://context7.com/fermi-ad/acsys-python/llms.txt Controls the lifecycle of data acquisition. Start begins the stream, while stop pauses it without clearing the configuration. ```APIDOC ## POST /dpm/start ### Description Begins streaming data for all configured device entries. ### Method POST ## POST /dpm/stop ### Description Pauses data acquisition for the current DPM context while preserving the device list. ### Method POST ``` -------------------------------- ### Reading Device Data via DPM Source: https://github.com/fermi-ad/acsys-python/wiki/acsys.dpm_tutorial Shows the full workflow for reading device data: establishing a DPMContext, adding a DRF request, starting acquisition, and iterating through replies. It handles data retrieval asynchronously using Python's async/await syntax. ```python #!/usr/bin/env python3 import logging import acsys.dpm FORMAT = '%(asctime)-15s [%(levelname)s] %(message)s' logging.basicConfig(format=FORMAT) log = logging.getLogger('acsys') log.setLevel(logging.INFO) async def my_app(con): # Setup context async with acsys.dpm.DPMContext(con) as dpm: # Add acquisition requests await dpm.add_entry(0, 'M:OUTTMP@p,1000') # Start acquisition await dpm.start() # Process incoming data async for ii in dpm.replies(): log.info(str(ii)) acsys.run_client(my_app) ``` -------------------------------- ### Parallel Requests with asyncio.gather Source: https://context7.com/fermi-ad/acsys-python/llms.txt Demonstrates concurrent execution of multiple asynchronous operations using Python's `asyncio.gather`. This example pings multiple nodes simultaneously and reports their status, improving efficiency for operations involving multiple targets. ```python import asyncio import acsys async def my_app(con): # Ping multiple nodes in parallel results = await asyncio.gather( con.ping('CENTRA'), con.ping('CLXSRV'), con.ping('DPM01') ) nodes = ['CENTRA', 'CLXSRV', 'DPM01'] for node, is_up in zip(nodes, results): status = 'UP' if is_up else 'DOWN' print(f'{node}: {status}') acsys.run_client(my_app) ``` -------------------------------- ### Sequential Async Pings Source: https://github.com/fermi-ad/acsys-python/wiki/acsys-tutorial Shows how to call multiple asynchronous ping operations sequentially. The second ping will only start after the first one has completed, highlighting that `await` blocks the current task. ```python async def my_app(con): result1 = await con.ping('CENTRA') result2 = await con.ping('CLXSRV') log.info('(%s, %s)', str(result1), str(result2)) ``` -------------------------------- ### Stream Live Device Data from Control System Source: https://context7.com/fermi-ad/acsys-python/llms.txt Streams real-time device data from the control system. Data flows continuously until explicitly stopped. This example shows how to add multiple devices with different acquisition rates and process incoming readings. ```python import acsys.dpm async def my_app(con): async with acsys.dpm.DPMContext(con) as dpm: # Add multiple devices with different rates await dpm.add_entry(0, 'M:OUTTMP@p,1000') # 1Hz periodic await dpm.add_entry(1, 'Z:CUBE_X.SETTING') # Default event await dpm.start() count = 0 async for item in dpm.replies(): if item.is_reading_for(0): print(f'Outside temp: {item.data}°F') elif item.is_reading_for(1): print(f'Cube X: {item.data}') count += 1 if count >= 10: break acsys.run_client(my_app) ``` -------------------------------- ### Data Logger Response Structure Source: https://github.com/fermi-ad/acsys-python/wiki/Example-DAQ-scripts An example of the JSON-like structure returned by the data logger. It contains the data points, associated timestamps (micros), and metadata describing the parameter. ```json { "tag": 0, "stamp": "2021-05-11 17:44:24.888000+00:00", "data": [53.35943970488751, 53.429207203857516], "meta": { "di": 27235, "name": "M:OUTTMP", "desc": "Outdoor temperature (F)", "units": "DegF" }, "micros": [1620754270342000, 1620754271342000] } ``` -------------------------------- ### DPM Context Manager Source: https://context7.com/fermi-ad/acsys-python/llms.txt Utilizes `acsys.dpm.DPMContext` as a context manager for establishing and managing connections to the Data Pool Manager (DPM) service. It supports automatic service discovery or explicit specification of a DPM node, ensuring proper connection setup and cleanup. ```python import acsys.dpm async def my_app(con): # Automatic DPM selection via service discovery async with acsys.dpm.DPMContext(con) as dpm: # DPM connection is ready to use print(f'Connected to DPM with list ID: {dpm.list_id}') # Connection automatically cleaned up # Or specify a particular DPM node async with acsys.dpm.DPMContext(con, dpm_node='DPM01') as dpm: print('Connected to DPM01') acsys.run_client(my_app) ``` -------------------------------- ### Initializing the ACSys client Source: https://github.com/fermi-ad/acsys-python/wiki/Home The run_client function serves as the entry point for ACSys scripts. It initializes the environment and executes the provided async main function with a connection object. ```python import acsys async def main(conn): # ACSys logic here pass acsys.run_client(main) ``` -------------------------------- ### Manage DPM lifecycle with DPMContext Source: https://github.com/fermi-ad/acsys-python/wiki/acsys.dpm Demonstrates the use of DPMContext as an asynchronous context manager to handle DPM resource allocation and cleanup. It is recommended to reuse the dpm object within loops to avoid the overhead of repeated network initialization. ```python async def my_app(con): async with DPMContext(con) as dpm: # Use 'dpm' object here. # 'dpm' resources are freed here return ``` -------------------------------- ### Initialize DPM Context Source: https://github.com/fermi-ad/acsys-python/wiki/acsys.dpm_tutorial Demonstrates how to establish a connection to a specific DPM node using an asynchronous context manager. ```python async with acsys.dpm.DPMContext(con, dpm_node='DPM01') as dpm: # The 'dpm' object is valid in this block and connected to DPM01. ``` -------------------------------- ### Apply periodic device settings Source: https://github.com/fermi-ad/acsys-python/wiki/Example-DAQ-scripts Shows how to perform periodic device settings by reading a current value and applying an incremented value. Requires valid Kerberos credentials and an authorized role. ```python import acsys.dpm async def my_app(con): # Setup context async with acsys.dpm.DPMContext(con) as dpm: # Check kerberos credentials and enable settings await dpm.enable_settings(role='testing') # Add acquisition requests await dpm.add_entry(0, 'Z:CUBE_X.SETTING@P,1H,true') # Start acquisition await dpm.start() # Process incoming data async for evt_res in dpm: if evt_res.is_reading_for(0): # Show current setting value print(evt_res) # Increment the current setting by 1 await dpm.apply_settings([(0, evt_res.data + 1)]) acsys.run_client(my_app) ``` -------------------------------- ### acsys.sync.get_available and acsys.sync.find_service Source: https://context7.com/fermi-ad/acsys-python/llms.txt Discovers available SYNC services for event monitoring. ```APIDOC ## acsys.sync.get_available and acsys.sync.find_service ### Description Discovers available SYNC services for event monitoring. ### Method GET ### Endpoint /sync/discover ### Parameters #### Query Parameters - **clock** (string) - Optional - The type of clock to filter services by (e.g., 'Clock_Tclk'). ### Request Example ```python import acsys.sync from acsys.sync.syncd_protocol import Clock_Tclk, Clock_Test async def my_app(con): nodes = await acsys.sync.get_available(con, clock=Clock_Tclk) print(f'TCLK SYNC services: {nodes}') node = await acsys.sync.find_service(con, clock=Clock_Tclk) if node: print(f'Using SYNC service at: {node}') acsys.run_client(my_app) ``` ### Response #### Success Response (200) - **available_services** (list of strings) - A list of available SYNC service endpoints. - **found_service** (string or null) - The endpoint of the found SYNC service, or null if not found. #### Response Example ```json { "available_services": ["sync_service_1", "sync_service_2"], "found_service": "sync_service_1" } ``` ``` -------------------------------- ### DPM.enable_settings Source: https://context7.com/fermi-ad/acsys-python/llms.txt Authenticates with DPM using Kerberos credentials to enable device settings. Requires a valid FNAL.GOV Kerberos ticket. ```APIDOC ## DPM.enable_settings ### Description Authenticates with DPM using Kerberos credentials to enable device settings. Requires valid FNAL.GOV Kerberos ticket. ### Method POST (Assumed, as it enables settings) ### Endpoint /dpm/enable_settings ### Parameters #### Query Parameters - **role** (string) - Required - The role for enabling settings (e.g., 'testing'). ### Request Example ```python import acsys.dpm async def my_app(con): async with acsys.dpm.DPMContext(con) as dpm: await dpm.enable_settings(role='testing') print('Settings enabled') acsys.run_client(my_app) ``` ### Response #### Success Response (200) Indicates successful authentication and enablement of settings. #### Response Example ```json { "status": "success", "message": "Settings enabled" } ``` ``` -------------------------------- ### Requesting and Processing Logger Data Source: https://github.com/fermi-ad/acsys-python/wiki/Example-DAQ-scripts Demonstrates how to initialize a DPM context, request a specific time range of data, and process incoming chunks asynchronously. Includes the necessary exit condition to stop the loop when the logger returns an empty data chunk. ```python import acsys.dpm results = { 'data': [], 'stamps': [] } async def my_app(con): async with acsys.dpm.DPMContext(con) as dpm: await dpm.add_entry(0, 'M:OUTTMP@P,1000,true<-LOGGER:1620754270000:1620754330000') await dpm.start() async for evt_res in dpm: if evt_res.is_reading_for(0): if evt_res.data == []: break else: results['data'] = results['data'] + evt_res.data results['stamps'] = results['stamps'] + evt_res.micros else: print(f'Status response: {evt_res}') acsys.run_client(my_app) print(results) ``` -------------------------------- ### DPM.apply_settings Source: https://context7.com/fermi-ad/acsys-python/llms.txt Sends setting values to control system devices. Requires a prior enable_settings call and device entries added with add_entry. ```APIDOC ## DPM.apply_settings ### Description Sends setting values to control system devices. Requires prior enable_settings call and device entries added with add_entry. ### Method POST (Assumed, as it applies settings) ### Endpoint /dpm/apply_settings ### Parameters #### Request Body - **settings** (array of tuples) - Required - A list of tuples, where each tuple contains a device index and its setting value. Example: `[(0, 42.0), (1, 43.0)]`. ### Request Example ```python import acsys.dpm async def my_app(con): async with acsys.dpm.DPMContext(con) as dpm: await dpm.enable_settings(role='testing') await dpm.add_entry(0, 'Z:CUBE_X.SETTING@N') await dpm.add_entry(1, 'Z:CUBE_Y.SETTING@N') await dpm.add_entry(2, 'Z:CUBE_Z.SETTING@N') await dpm.apply_settings([ (0, 42.0), # Set CUBE_X to 42 (1, 43.0), # Set CUBE_Y to 43 (2, 44.0) # Set CUBE_Z to 44 ]) print('Settings applied') acsys.run_client(my_app) ``` ### Response #### Success Response (200) Indicates that the settings have been successfully sent to the devices. #### Response Example ```json { "status": "success", "message": "Settings applied" } ``` ``` -------------------------------- ### Retrieve Historical Data from Data Logger Source: https://context7.com/fermi-ad/acsys-python/llms.txt Retrieves historical data from the data logger using timestamp ranges. Data arrives in chunks of 487 points. The format for requesting logged data includes the device, event, and a start and end timestamp in milliseconds. ```python import acsys.dpm async def my_app(con): results = {'data': [], 'timestamps': []} async with acsys.dpm.DPMContext(con) as dpm: # Request 60 seconds of logged data # Format: device@event<-LOGGER:start_ms:end_ms await dpm.add_entry(0, 'M:OUTTMP@P,1000,true<-LOGGER:1620754270000:1620754330000') await dpm.start() async for item in dpm.replies(): if item.is_reading_for(0): # Empty data signals end of historical data if item.data == []: break results['data'].extend(item.data) results['timestamps'].extend(item.micros) else: print(f'Status: {item}') print(f'Retrieved {len(results["data"])} data points') # Output: Retrieved 60 data points acsys.run_client(my_app) ``` -------------------------------- ### Discover Available SYNC Services Source: https://context7.com/fermi-ad/acsys-python/llms.txt Discovers available SYNC services for event monitoring. `get_available` finds all SYNC services of a specific clock type, while `find_service` locates a single SYNC service. ```python import acsys.sync from acsys.sync.syncd_protocol import Clock_Tclk, Clock_Test async def my_app(con): # Find all SYNC services for TCLK nodes = await acsys.sync.get_available(con, clock=Clock_Tclk) print(f'TCLK SYNC services: {nodes}') # Find single SYNC service node = await acsys.sync.find_service(con, clock=Clock_Tclk) if node: print(f'Using SYNC service at: {node}') acsys.run_client(my_app) ``` -------------------------------- ### Run ACSys Client Script Source: https://context7.com/fermi-ad/acsys-python/llms.txt Provides the entry point for executing ACSys client scripts. It takes an asynchronous function that accepts a Connection object and runs it within the ACSys framework, blocking until the function completes. ```python import acsys async def my_app(con): print(f'Assigned handle: {con.handle}') # Your async code here # Run the client - blocks until my_app completes acsys.run_client(my_app) ``` -------------------------------- ### Configure DPM Data Source Requests Source: https://github.com/fermi-ad/acsys-python/wiki/Data-sources Demonstrates how to append a data source to a DRF request string and how to utilize the .start method in the ACSYS Python client. The .start method serves as a default source for requests that do not explicitly define one. ```python # Example of appending a data source to a request string request_string = "G:AMANDA@P,30000,TRUE<-LOGGERDURATION:3600000:MCR" # Example of using the .start method with a data source dpm.start('LOGGERDURATION:3600000:MCR') ``` -------------------------------- ### Initialize ACNET Client Source: https://github.com/fermi-ad/acsys-python/wiki/acsys-tutorial Demonstrates the basic structure of an acsys client script. It defines an asynchronous main function and uses acsys.run_client to establish a connection. ```python #!/usr/bin/env python3 import acsys async def my_app(con): print(f'Assigned handle {con.handle}.') acsys.run_client(my_app) ``` -------------------------------- ### Basic Async Function with Await Source: https://github.com/fermi-ad/acsys-python/wiki/acsys-tutorial Demonstrates a simple asynchronous function that pings a node. It uses `await` to handle the asynchronous `ping` method, behaving like synchronous code when no other tasks are running. ```python async def my_app(con): node = 'CENTRA' if await con.ping(node): log.info('%s replied', node) else: log.warning('%s not responding', node) acsys.run_client(my_app) ``` -------------------------------- ### Perform high-rate data acquisition via FTP Source: https://github.com/fermi-ad/acsys-python/wiki/Example-DAQ-scripts Demonstrates how to request high-rate data (faster than 15Hz) using the FTP source in an acsys-python DPM context. It initializes the context, adds an entry with the FTP modifier, and iterates through incoming data. ```python import acsys.dpm async def my_app(con): # Setup context async with acsys.dpm.DPMContext(con) as dpm: # Add acquisition requests # This is a request for 60 seconds of data await dpm.add_entry(0, 'M:OUTTMP@P,20H,true<-FTP') # Start acquisition await dpm.start() # Process incoming data async for evt_res in dpm: if evt_res.is_reading_for(0): print(evt_res) acsys.run_client(my_app) ``` -------------------------------- ### Implement ACNET Ping Helper Method Source: https://github.com/fermi-ad/acsys-python/wiki/acsys-tutorial Demonstrates how to implement a ping method using request_reply. The implementation includes error handling to return False specifically on ACNET timeout statuses. ```python async def ping(self, node): try: await self.request_reply('ACNET@' + node, b'\x00\x00', timeout=250) return True except acsys.status.Status as e: if e == acsys.status.ACNET_UTIME: return False else: raise ``` -------------------------------- ### Enable DPM Settings with Kerberos Authentication Source: https://context7.com/fermi-ad/acsys-python/llms.txt Authenticates with DPM using Kerberos credentials to enable device settings. Requires a valid FNAL.GOV Kerberos ticket. This function is a prerequisite for applying settings. ```python import acsys.dpm async def my_app(con): async with acsys.dpm.DPMContext(con) as dpm: # Enable settings with a specific role await dpm.enable_settings(role='testing') print('Settings enabled') # Now apply_settings can be used acsys.run_client(my_app) ``` -------------------------------- ### Configure Python Logging for ACSys Source: https://context7.com/fermi-ad/acsys-python/llms.txt Shows how to integrate the standard Python logging module with ACSys. This allows developers to track script execution and debug connectivity issues by setting the appropriate log level. ```python import logging import acsys # Configure logging format and level FORMAT = '%(asctime)-15s [%(levelname)s] %(message)s' logging.basicConfig(format=FORMAT) log = logging.getLogger('acsys') log.setLevel(logging.INFO) # Use DEBUG for more detail async def my_app(con): log.info('Script started with handle %s', con.handle) # Your code here acsys.run_client(my_app) ``` -------------------------------- ### Querying Available DPMs Source: https://github.com/fermi-ad/acsys-python/wiki/acsys.dpm_tutorial Demonstrates how to use the acsys.dpm.available_dpms function to discover DPMs on the network. This is primarily a diagnostic tool as the API typically handles service discovery automatically. ```python #!/usr/bin/env python3 import logging import acsys.dpm FORMAT = '%(asctime)-15s [%(levelname)s] %(message)s' logging.basicConfig(format=FORMAT) log = logging.getLogger('acsys') log.setLevel(logging.INFO) async def my_app(con): dpms = await acsys.dpm.available_dpms(con) log.info('available DPMs: %s', str(dpms)) acsys.run_client(my_app) ``` -------------------------------- ### Register and monitor SYNC events in Python Source: https://github.com/fermi-ad/acsys-python/wiki/acsys.sync Demonstrates how to use the acsys.sync.get_events function to register for specific clock events and process them asynchronously as they arrive. This requires an active acsys connection and the event identifiers in DRF2 format. ```python #!/usr/bin/env python3 import asyncio import logging import acsys.sync FORMAT = '%(asctime)-15s [%(levelname)s] %(message)s' logging.basicConfig(format=FORMAT) log = logging.getLogger('acsys') log.setLevel(logging.INFO) async def my_client(con): async for ii in acsys.sync.get_events(con, ['e,2', 'e,8f']): log.info('%s', str(ii)) acsys.run_client(my_client) ``` -------------------------------- ### Apply Settings to DPM Devices Source: https://context7.com/fermi-ad/acsys-python/llms.txt Sends setting values to control system devices. Requires a prior enable_settings call and device entries added with add_entry. This function allows modification of device parameters. ```python import acsys.dpm async def my_app(con): async with acsys.dpm.DPMContext(con) as dpm: await dpm.enable_settings(role='testing') # Add devices for settings (use 'N' event for setting-only) await dpm.add_entry(0, 'Z:CUBE_X.SETTING@N') await dpm.add_entry(1, 'Z:CUBE_Y.SETTING@N') await dpm.add_entry(2, 'Z:CUBE_Z.SETTING@N') # Apply settings to multiple devices await dpm.apply_settings([ (0, 42.0), # Set CUBE_X to 42 (1, 43.0), # Set CUBE_Y to 43 (2, 44.0) # Set CUBE_Z to 44 ]) print('Settings applied') acsys.run_client(my_app) ``` -------------------------------- ### Configure Logging for ACNET Source: https://github.com/fermi-ad/acsys-python/wiki/acsys-tutorial Shows how to integrate Python's standard logging module with the acsys package. This is recommended for debugging and monitoring connection status. ```python #!/usr/bin/env python3 import asyncio import logging import acsys # Initialize the logger. Set the minimum reported level to INFO. FORMAT = '%(asctime)-15s [%(levelname)s] %(message)s' logging.basicConfig(format=FORMAT) log = logging.getLogger('acsys') log.setLevel(logging.INFO) # Define starting function. async def my_app(con): log.info('Assigned handle %s', con.handle) acsys.run_client(my_app) ``` -------------------------------- ### DPM.add_entry and DPM.add_entries Source: https://context7.com/fermi-ad/acsys-python/llms.txt Adds device acquisition requests to the DPM using DRF2 format strings. Each entry is tagged with an integer for identifying responses. ```APIDOC ## POST /dpm/add_entry ### Description Adds a single device acquisition request to the DPM context. ### Method POST ### Parameters #### Request Body - **tag** (int) - Required - Unique identifier for the request. - **drf_string** (string) - Required - The DRF2 format string for the device. ### Response #### Success Response (200) - **status** (bool) - Returns True if successfully added. ## POST /dpm/add_entries ### Description Adds multiple device acquisition requests in a single batch operation. ### Method POST ### Parameters #### Request Body - **entries** (list) - Required - A list of tuples containing (tag, drf_string). ### Response #### Success Response (200) - **errors** (list) - Returns a list of (tag, status) for any failed entries. ``` -------------------------------- ### get_available Function Source: https://github.com/fermi-ad/acsys-python/wiki/acsys.sync Retrieves a list of nodes running the SYNC service for a given clock. ```APIDOC ## get_available Function ### Description Returns a list of ACNET nodes that are currently running the SYNC service for a specified clock. ### Method `get_available(con, clock)` ### Endpoint N/A ### Parameters #### Parameters - **con** (`acsys.Connection`) - An `acsys.Connection` object. - **clock** (enum) - Specifies which clock to monitor (see `find_service()` for valid options). ### Request Example ```python # Assuming 'con' is an established acsys.Connection object available_nodes = acsys.sync.get_available(con, syncd_protocol.Clock_CMTF) print(f"Nodes running SYNC service for Clock_CMTF: {available_nodes}") ``` ### Response #### Success Response (200) - **Return Value** (list[str]) - A list of strings, where each string is the name of an ACNET node running the SYNC service for the specified clock. ``` -------------------------------- ### Handle ACSys Status Exceptions Source: https://context7.com/fermi-ad/acsys-python/llms.txt Demonstrates how to catch and inspect acsys.status.Status exceptions. It shows how to extract facility and error codes, determine error severity, and check for specific ACNET status constants. ```python import acsys import acsys.status async def my_app(con): try: # Attempt a request that might fail await con.request_reply('ACNET@BADNODE', b'\x00\x00', timeout=500) except acsys.status.Status as e: print(f'Status code: {e}') print(f' Facility: {e.facility}') print(f' Error code: {e.err_code}') if e.is_fatal: print(' This is a fatal error') elif e.is_warning: print(' This is a warning') elif e.is_success: print(' This is a success status') # Check for specific errors if e == acsys.status.ACNET_UTIME: print(' Request timed out') elif e == acsys.status.ACNET_NO_NODE: print(' Node not found') acsys.run_client(my_app) ``` -------------------------------- ### DPM Combined Reading and Setting Source: https://context7.com/fermi-ad/acsys-python/llms.txt Reads device values and applies settings in the same acquisition loop based on incoming data. ```APIDOC ## Combined Reading and Setting ### Description Reads device values and applies settings in the same acquisition loop based on incoming data. ### Method GET/POST (Implied by reading and setting operations) ### Endpoint /dpm/acquisition_loop ### Parameters #### Request Body (Implicit via add_entry) - **entry_id** (integer) - Required - Unique identifier for the device entry. - **device_name** (string) - Required - The name of the device to read or set (e.g., 'Z:RMTEMP@p,10s', 'Z:RMFAN.SETTING@N'). ### Request Example ```python import acsys.dpm async def my_app(con): async with acsys.dpm.DPMContext(con) as dpm: await dpm.enable_settings(role='testing') await dpm.add_entry(0, 'Z:RMTEMP@p,10s') await dpm.add_entry(1, 'Z:RMFAN.SETTING@N') await dpm.start() async for item in dpm.replies(): if item.is_reading_for(0): temp = item.data fan_duty = min(max((temp - 70) * 5.0, 0), 100) print(f'Temperature: {temp}°F -> Fan: {fan_duty}%') await dpm.apply_settings([(1, fan_duty)]) elif item.is_status_for(1): if item.status.is_fatal: print(f'Setting error: {item.status}') acsys.run_client(my_app) ``` ### Response #### Success Response (200) An async generator yielding `ClockEvent` and `StateEvent` objects. #### Response Example ```json { "event_type": "reading", "entry_id": 0, "data": 75.5, "timestamp": "2023-10-27T10:00:00Z" } ``` ```json { "event_type": "status", "entry_id": 1, "status": { "is_fatal": false, "code": 0 }, "timestamp": "2023-10-27T10:00:01Z" } ``` ``` -------------------------------- ### Combined Reading and Setting in DPM Acquisition Loop Source: https://context7.com/fermi-ad/acsys-python/llms.txt Reads device values and applies settings within the same acquisition loop, reacting to incoming data. This enables dynamic control based on real-time measurements. ```python import acsys.dpm async def my_app(con): async with acsys.dpm.DPMContext(con) as dpm: await dpm.enable_settings(role='testing') # Read temperature every 10 seconds await dpm.add_entry(0, 'Z:RMTEMP@p,10s') # Fan setting (N event = no readings, settings only) await dpm.add_entry(1, 'Z:RMFAN.SETTING@N') await dpm.start() async for item in dpm.replies(): if item.is_reading_for(0): # Calculate fan speed based on temperature temp = item.data fan_duty = min(max((temp - 70) * 5.0, 0), 100) print(f'Temperature: {temp}°F -> Fan: {fan_duty}%') await dpm.apply_settings([(1, fan_duty)]) elif item.is_status_for(1): if item.status.is_fatal: print(f'Setting error: {item.status}') acsys.run_client(my_app) ``` -------------------------------- ### acsys.sync.get_events Source: https://context7.com/fermi-ad/acsys-python/llms.txt Monitors clock and state events in soft real-time. Returns an async generator yielding ClockEvent and StateEvent objects. ```APIDOC ## acsys.sync.get_events ### Description Monitors clock and state events in soft real-time. Returns an async generator yielding ClockEvent and StateEvent objects. ### Method GET ### Endpoint /sync/events ### Parameters #### Query Parameters - **event_filters** (list of strings) - Required - A list of event filters (e.g., 'e,2', 'e,8f'). ### Request Example ```python import acsys.sync async def my_app(con): async for event in acsys.sync.get_events(con, ['e,2', 'e,8f']): if hasattr(event, 'event'): # ClockEvent print(f'Clock event {hex(event.event)} at {event.stamp}') print(f' Instance number: {event.number}') else: # StateEvent print(f'State event DI:{event.di} value:{event.value} at {event.stamp}') acsys.run_client(my_app) ``` ### Response #### Success Response (200) An async generator yielding `ClockEvent` and `StateEvent` objects. #### Response Example ```json { "event_type": "clock", "event": "0x2", "stamp": "2023-10-27T10:00:00Z", "number": 1 } ``` ```json { "event_type": "state", "di": 10, "value": 1, "stamp": "2023-10-27T10:00:01Z" } ``` ``` -------------------------------- ### Discover Available DPM Services Source: https://context7.com/fermi-ad/acsys-python/llms.txt Service discovery functions to locate available DPM instances on the network. `available_dpms` returns a list of all DPMs, while `find_dpm` can find a single DPM or a specific one by node name. ```python import acsys.dpm async def my_app(con): # Find all available DPMs dpms = await acsys.dpm.available_dpms(con) print(f'Available DPMs: {dpms}') # Output: Available DPMs: ['DPM01', 'DPM04', 'DPM05'] # Find a single DPM dpm_node = await acsys.dpm.find_dpm(con) print(f'Selected DPM: {dpm_node}') # Check if specific DPM is running specific = await acsys.dpm.find_dpm(con, node='DPM01') if specific: print('DPM01 is available') acsys.run_client(my_app) ``` -------------------------------- ### Concurrent Async Pings with asyncio.gather Source: https://github.com/fermi-ad/acsys-python/wiki/acsys-tutorial Illustrates how to execute multiple asynchronous operations concurrently using `asyncio.gather` and `asyncio.Task`. This allows pings to be sent in parallel, improving efficiency. ```python import asyncio async def my_app(con): a, b = await asyncio.gather( asyncio.Task(con.ping('CENTRA')), asyncio.Task(con.ping('CLXSRV')) ) log.info('(%s, %s)', str(a), str(a)) ``` -------------------------------- ### Concurrent Periodic Async Tasks Source: https://github.com/fermi-ad/acsys-python/wiki/acsys-tutorial Demonstrates running multiple, independent asynchronous tasks concurrently using `asyncio.gather`. Each task runs in a loop, performing periodic checks without blocking others. ```python import asyncio async def ping_periodically(con, node, delay): while True: await asyncio.sleep(delay) if await con.ping(node): log.info('%s is still up', node) else: log.warning('%s is not responding', node) async def my_app(con): await asyncio.gather( asyncio.Task(ping_periodically(con, 'CLXSRV', 0.5)), asyncio.Task(ping_periodically(con, 'CENTRA', 0.333)) ) ``` -------------------------------- ### find_service Function Source: https://github.com/fermi-ad/acsys-python/wiki/acsys.sync Searches for an available SYNC service on a specified clock. ```APIDOC ## find_service Function ### Description Searches for an available SYNC service on a specified clock. It can optionally target a specific ACNET node. ### Method `find_service(con, clock, node=None)` ### Endpoint N/A ### Parameters #### Parameters - **con** (`acsys.Connection`) - An `acsys.Connection` object. - **clock** (enum) - Specifies which clock to monitor. Can be one of `syncd_protocol.Clock_Tclk`, `syncd_protocol.Clock_Test`, `syncd_protocol.Clock_CMTF`, or `syncd_protocol.Clock_NML`. Defaults to `Clock_Tclk`. - **node** (str, optional) - If `None`, searches for any available node running the SYNC service. If a string, it should be the name of an ACNET node to target. Defaults to `None`. ### Request Example ```python # Assuming 'con' is an established acsys.Connection object sync_node = acsys.sync.find_service(con, syncd_protocol.Clock_Tclk) if sync_node: print(f"SYNC service found on node: {sync_node}") else: print("No SYNC service found.") ``` ### Response #### Success Response (200) - **Return Value** (str or None) - A string representing the ACNET node running the SYNC service, or `None` if no service was found. ``` -------------------------------- ### acsys.dpm.available_dpms and acsys.dpm.find_dpm Source: https://context7.com/fermi-ad/acsys-python/llms.txt Service discovery functions to locate available DPM instances on the network. ```APIDOC ## acsys.dpm.available_dpms and acsys.dpm.find_dpm ### Description Service discovery functions to locate available DPM instances on the network. ### Method GET ### Endpoint /dpm/discover ### Parameters #### Query Parameters - **node** (string) - Optional - The specific DPM node name to find. ### Request Example ```python import acsys.dpm async def my_app(con): dpms = await acsys.dpm.available_dpms(con) print(f'Available DPMs: {dpms}') dpm_node = await acsys.dpm.find_dpm(con) print(f'Selected DPM: {dpm_node}') specific = await acsys.dpm.find_dpm(con, node='DPM01') if specific: print('DPM01 is available') acsys.run_client(my_app) ``` ### Response #### Success Response (200) - **available_dpms** (list of strings) - A list of available DPM instance names. - **found_dpm** (string or null) - The name of the found DPM instance, or null if not found. #### Response Example ```json { "available_dpms": ["DPM01", "DPM04", "DPM05"], "found_dpm": "DPM01" } ``` ``` -------------------------------- ### Request High-Rate Data using FTP Source Source: https://context7.com/fermi-ad/acsys-python/llms.txt Requests data at rates faster than 15Hz using the FTP data source. This allows for acquiring data at higher frequencies than typically supported by standard DPM requests. ```python import acsys.dpm async def my_app(con): async with acsys.dpm.DPMContext(con) as dpm: # Request data at 20Hz using FTP source await dpm.add_entry(0, 'M:OUTTMP@P,20H,true<-FTP') await dpm.start() count = 0 async for item in dpm.replies(): if item.is_reading_for(0): print(f'High-rate reading: {item.data}') count += 1 if count >= 100: break acsys.run_client(my_app) ``` -------------------------------- ### Monitor SYNC Clock and State Events Source: https://context7.com/fermi-ad/acsys-python/llms.txt Monitors clock and state events in soft real-time using the SYNC service. It returns an async generator yielding ClockEvent and StateEvent objects, allowing for real-time event processing. ```python import acsys.sync async def my_app(con): # Monitor TCLK events $02 and $8F async for event in acsys.sync.get_events(con, ['e,2', 'e,8f']): if hasattr(event, 'event'): # ClockEvent print(f'Clock event {hex(event.event)} at {event.stamp}') print(f' Instance number: {event.number}') else: # StateEvent print(f'State event DI:{event.di} value:{event.value} at {event.stamp}') acsys.run_client(my_app) ```