Skip to content
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion lib/py-edge-driver/LICENSE
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
MIT License

Copyright (c) 2025 AMRC-FactoryPlus
Copyright (c) University of Sheffield AMRC 2025

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
Expand Down
18 changes: 9 additions & 9 deletions lib/py-edge-driver/poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

14 changes: 12 additions & 2 deletions lib/py-edge-driver/pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
[project]
name = "py-edge-driver"
name = "amrc.factoryplus.edge_driver"
version = "0.1.0"
description = "An Edge Agent driver library for Factory+."
authors = [{ name = "KavanPrice", email = "[email protected]" }]
license = { text = "MIT" }
license = "MIT"
readme = "README.md"
requires-python = ">=3.13"
dependencies = ["paho-mqtt (>=2.1.0,<3.0.0)", "asyncio (>=3.4.3,<4.0.0)"]
Expand All @@ -13,11 +13,21 @@ dependencies = ["paho-mqtt (>=2.1.0,<3.0.0)", "asyncio (>=3.4.3,<4.0.0)"]
requires = ["poetry-core>=2.0.0,<3.0.0"]
build-backend = "poetry.core.masonry.api"

[tool.poetry]
packages = [
{ include = "amrc", from = "src" }
]

[tool.poetry.group.dev.dependencies]
pytest = "^8.3.5"
pytest-mock = "^3.14.0"
pytest-asyncio = "^0.26.0"

[tool.poetry.group.test.dependencies]
pytest = "^8.3.5"
pytest-mock = "^3.14.0"
pytest-asyncio = "^0.26.0"

[tool.pytest.ini_options]
asyncio_mode = "auto"
asyncio_default_fixture_loop_scope = "function"
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
# Copyright (c) University of Sheffield AMRC 2025.
"""
AMRC Connectivity Stack - Python Edge Driver Library
This library provides a Python interface for writing edge device drivers for the ACS Edge Agent.
"""
from .async_driver import AsyncDriver
from .polled_driver import PolledDriver
from .handler_protocol import HandlerProtocol
from . import bufferx as BufferX
# Copyright (c) University of Sheffield AMRC 2025.

"""
AMRC Connectivity Stack - Python Edge Driver Library

This library provides a Python interface for writing edge device drivers for the ACS Edge Agent.
"""

from .async_driver import AsyncDriver
from .polled_driver import PolledDriver
from .handler import Handler
from . import bufferx as BufferX
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# Copyright (c) University of Sheffield AMRC 2025.

import logging

from typing import Dict, Union, Type

from .driver import Driver

from .handler import Handler


class AsyncDriver(Driver):
def __init__(
self,
handler: Type[Handler],
edge_username: str,
edge_mqtt: str,
edge_password: str,
reconnect_delay: int = 500,
):
"""
Initialize the Async Driver with the provided options.

Args:
handler: Handler class for device-specific logic
edge_username: Username to connect to edge MQTT
edge_mqtt: Edge MQTT host
edge_password: Password to connect to edge MQTT
reconnect_delay: Delay in reconnecting to the southbound device
"""
super().__init__(
handler, edge_username, edge_mqtt, edge_password, reconnect_delay
)

async def data(self, spec, buf):
self.log.debug(f"DATA {buf}")
dtopic = self.topics.get(spec) if self.topics else None
if not dtopic:
return

mtopic = self.topic("data", dtopic)
return self.publish_async(mtopic, buf)

def publish_async(
self, topic: str, payload: Union[str, bytes, bytearray, int, float, None]
):
return self.mqtt.publish(topic, payload)
Original file line number Diff line number Diff line change
@@ -1,83 +1,91 @@
# Copyright (c) University of Sheffield AMRC 2025.

import struct
import json

def fromInt8(value) -> bytes:
return struct.pack('<b', value)

def fromUInt8(value) -> bytes:
return struct.pack('<B', value)

def fromInt16LE(value) -> bytes:
return struct.pack('<h', value)

def fromInt16BE(value) -> bytes:
return struct.pack('>h', value)

def fromUInt16LE(value) -> bytes:
return struct.pack('<H', value)

def fromUInt16BE(value) -> bytes:
return struct.pack('>H', value)

def fromInt32LE(value) -> bytes:
return struct.pack('<i', value)

def fromInt32BE(value) -> bytes:
return struct.pack('>i', value)

def fromUInt32LE(value) -> bytes:
return struct.pack('<I', value)

def fromUInt32BE(value) -> bytes:
return struct.pack('>I', value)

def fromBigInt64LE(value) -> bytes:
return struct.pack('<q', value)

def fromBigInt64BE(value) -> bytes:
return struct.pack('>q', value)

def fromBigUInt64LE(value) -> bytes:
return struct.pack('<Q', value)

def fromBigUInt64BE(value) -> bytes:
return struct.pack('>Q', value)

def fromFloatLE(value) -> bytes:
return struct.pack('<f', value)

def fromFloatBE(value) -> bytes:
return struct.pack('>f', value)

def fromDoubleLE(value) -> bytes:
return struct.pack('<d', value)

def fromDoubleBE(value) -> bytes:
return struct.pack('>d', value)

def fromJSON(value) -> bytes:
return json.dumps(value).encode('utf-8')

__all__ = [
'fromInt8',
'fromUInt8',
'fromInt16LE',
'fromInt16BE',
'fromUInt16LE',
'fromUInt16BE',
'fromInt32LE',
'fromInt32BE',
'fromUInt32LE',
'fromUInt32BE',
'fromBigInt64LE',
'fromBigInt64BE',
'fromBigUInt64LE',
'fromBigUInt64BE',
'fromFloatLE',
'fromFloatBE',
'fromDoubleLE',
'fromDoubleBE',
'fromJSON',
]
# Copyright (c) University of Sheffield AMRC 2025.

"""
Binary packing utilities.

These are convenience wrappers around struct.pack() for common data types.

Familiar users can use `struct.pack()` directly: `fromInt32LE(42)` is equivalent to `struct.pack('<i', 42)`
"""

import struct
import json

def fromInt8(value) -> bytes:
return struct.pack('<b', value)

def fromUInt8(value) -> bytes:
return struct.pack('<B', value)

def fromInt16LE(value) -> bytes:
return struct.pack('<h', value)

def fromInt16BE(value) -> bytes:
return struct.pack('>h', value)

def fromUInt16LE(value) -> bytes:
return struct.pack('<H', value)

def fromUInt16BE(value) -> bytes:
return struct.pack('>H', value)

def fromInt32LE(value) -> bytes:
return struct.pack('<i', value)

def fromInt32BE(value) -> bytes:
return struct.pack('>i', value)

def fromUInt32LE(value) -> bytes:
return struct.pack('<I', value)

def fromUInt32BE(value) -> bytes:
return struct.pack('>I', value)

def fromBigInt64LE(value) -> bytes:
return struct.pack('<q', value)

def fromBigInt64BE(value) -> bytes:
return struct.pack('>q', value)

def fromBigUInt64LE(value) -> bytes:
return struct.pack('<Q', value)

def fromBigUInt64BE(value) -> bytes:
return struct.pack('>Q', value)

def fromFloatLE(value) -> bytes:
return struct.pack('<f', value)

def fromFloatBE(value) -> bytes:
return struct.pack('>f', value)

def fromDoubleLE(value) -> bytes:
return struct.pack('<d', value)

def fromDoubleBE(value) -> bytes:
return struct.pack('>d', value)

def fromJSON(value) -> bytes:
return json.dumps(value).encode('utf-8')

__all__ = [
'fromInt8',
'fromUInt8',
'fromInt16LE',
'fromInt16BE',
'fromUInt16LE',
'fromUInt16BE',
'fromInt32LE',
'fromInt32BE',
'fromUInt32LE',
'fromUInt32BE',
'fromBigInt64LE',
'fromBigInt64BE',
'fromBigUInt64LE',
'fromBigUInt64BE',
'fromFloatLE',
'fromFloatBE',
'fromDoubleLE',
'fromDoubleBE',
'fromJSON',
]
Loading