Skip to content

Commit 87c9ebf

Browse files
initial joining of OSHConnect with previous dep csapi4py
1 parent 111c7a4 commit 87c9ebf

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

53 files changed

+3386
-367
lines changed

oshconnect/control.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,9 @@
55
# Contact Email: ian@botts-inc.com
66
# ==============================================================================
77
import websockets
8-
from consys4py.comm.mqtt import MQTTCommClient
9-
from consys4py.datamodels.commands import CommandJSON
10-
from consys4py.datamodels.control_streams import ControlStreamJSONSchema
8+
from oshconnect.csapi4py.comm.mqtt import MQTTCommClient
9+
from oshconnect.datamodels.commands import CommandJSON
10+
from oshconnect.datamodels.control_streams import ControlStreamJSONSchema
1111

1212
from oshconnect.osh_connect_datamodels import System
1313

oshconnect/core_datamodels.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,9 @@
88

99
from typing import List
1010

11-
from consys4py.datamodels.swe_components import GeometrySchema
12-
from consys4py.datamodels.datastreams import DatastreamSchema
13-
from consys4py.datamodels.api_utils import Link
11+
from oshconnect.datamodels.geometry import Geometry
12+
from oshconnect.datamodels.datastreams import DatastreamSchema
13+
from oshconnect.datamodels.api_utils import Link
1414
from pydantic import BaseModel, ConfigDict, Field, SerializeAsAny
1515
from shapely import Point
1616

@@ -94,7 +94,7 @@ class SystemResource(BaseModel):
9494
feature_type: str = Field(None, serialization_alias="type")
9595
system_id: str = Field(None, serialization_alias="id")
9696
properties: dict = Field(None)
97-
geometry: GeometrySchema | None = Field(None)
97+
geometry: Geometry | None = Field(None)
9898
bbox: BoundingBox = Field(None)
9999
links: List[Link] = Field(None)
100100
description: str = Field(None)

oshconnect/csapi4py/__init__.py

Whitespace-only changes.

oshconnect/csapi4py/comm/__init__.py

Whitespace-only changes.

oshconnect/csapi4py/comm/mqtt.py

Lines changed: 197 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,197 @@
1+
import paho.mqtt.client as mqtt
2+
3+
4+
class MQTTCommClient:
5+
def __init__(self, url, port=1883, username=None, password=None, path='mqtt', client_id="", transport='tcp'):
6+
"""
7+
Wraps a paho mqtt client to provide a simple interface for interacting with the mqtt server that is customized
8+
for this library.
9+
10+
:param url: url of the mqtt server
11+
:param port: port the mqtt server is communicating over, default is 1883 or whichever port the main node is
12+
using if in websocket mode
13+
:param username: used if node is requiring authentication to access this service
14+
:param password: used if node is requiring authentication to access this service
15+
:param path: used for setting the path when using websockets (usually sensorhub/mqtt by default)
16+
"""
17+
self.__url = url
18+
self.__port = port
19+
self.__path = path
20+
self.__client_id = client_id
21+
self.__transport = transport
22+
23+
self.__client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2, client_id=client_id)
24+
25+
if self.__transport == 'websockets':
26+
self.__client.ws_set_options(path=self.__path)
27+
28+
if username is not None and password is not None:
29+
self.__client.username_pw_set(username, password)
30+
self.__client.tls_set(tls_version=mqtt.ssl.PROTOCOL_TLSv1_2)
31+
32+
self.__client.on_connect = self.on_connect
33+
self.__client.on_subscribe = self.on_subscribe
34+
self.__client.on_message = self.on_message
35+
self.__client.on_publish = self.on_publish
36+
self.__client.on_log = self.on_log
37+
self.__client.on_disconnect = self.on_disconnect
38+
39+
self.__is_connected = False
40+
41+
@staticmethod
42+
def on_connect(client, userdata, flags, rc, properties):
43+
print(f'Connected with result code: {rc}')
44+
print(f'{properties}')
45+
46+
@staticmethod
47+
def on_subscribe(client, userdata, mid, granted_qos, properties):
48+
print(f'Subscribed: {mid} {granted_qos}')
49+
50+
@staticmethod
51+
def on_message(client, userdata, msg):
52+
print(f'{msg.payload.decode("utf-8")}')
53+
54+
@staticmethod
55+
def on_publish(client, userdata, mid, info, properties):
56+
print(f'Published: {mid}')
57+
58+
@staticmethod
59+
def on_log(client, userdata, level, buf):
60+
print(f'Log: {buf}')
61+
62+
@staticmethod
63+
def on_disconnect(client, userdata, dc_flag, rc, properties):
64+
print(f'Client {client} disconnected: {dc_flag} {rc}')
65+
66+
def connect(self, keepalive=60):
67+
# print(f'Connecting to {self.__url}:{self.__port}')
68+
self.__client.connect(self.__url, self.__port, keepalive=keepalive)
69+
70+
def subscribe(self, topic, qos=0, msg_callback=None):
71+
"""
72+
Subscribe to a topic, and optionally set a callback for when a message is received on that topic. To actually
73+
retrieve any information you must set a callback.
74+
75+
:param topic: MQTT topic to subscribe to (example/topic)
76+
:param qos: quality of service, 0, 1, or 2
77+
:param msg_callback: callback with the form: callback(client, userdata, msg)
78+
:return:
79+
"""
80+
self.__client.subscribe(topic, qos)
81+
if msg_callback is not None:
82+
self.__client.message_callback_add(topic, msg_callback)
83+
84+
def publish(self, topic, payload=None, qos=0, retain=False):
85+
self.__client.publish(topic, payload, qos, retain=retain)
86+
87+
def unsubscribe(self, topic):
88+
self.__client.unsubscribe(topic)
89+
90+
def disconnect(self):
91+
self.__client.disconnect()
92+
93+
def set_on_connect(self, on_connect):
94+
"""
95+
Set the on_connect callback for the MQTT client.
96+
97+
:param on_connect:
98+
:return:
99+
"""
100+
self.__client.on_connect = on_connect
101+
102+
def set_on_disconnect(self, on_disconnect):
103+
"""
104+
Set the on_disconnect callback for the MQTT client.
105+
106+
:param on_disconnect:
107+
:return:
108+
"""
109+
self.__client.on_disconnect = on_disconnect
110+
111+
def set_on_subscribe(self, on_subscribe):
112+
"""
113+
Set the on_subscribe callback for the MQTT client.
114+
115+
:param on_subscribe:
116+
:return:
117+
"""
118+
self.__client.on_subscribe = on_subscribe
119+
120+
def set_on_unsubscribe(self, on_unsubscribe):
121+
"""
122+
Set the on_unsubscribe callback for the MQTT client.
123+
124+
:param on_unsubscribe:
125+
:return:
126+
"""
127+
self.__client.on_unsubscribe = on_unsubscribe
128+
129+
def set_on_publish(self, on_publish):
130+
"""
131+
Set the on_publish callback for the MQTT client.
132+
133+
:param on_publish:
134+
:return:
135+
"""
136+
self.__client.on_publish = on_publish
137+
138+
def set_on_message(self, on_message):
139+
"""
140+
Set the on_message callback for the MQTT client. It is recommended to set individual callbacks for each
141+
subscribed topic.
142+
143+
:param on_message:
144+
:return:
145+
"""
146+
self.__client.on_message = on_message
147+
148+
def set_on_log(self, on_log):
149+
"""
150+
Set the on_log callback for the MQTT client.
151+
152+
:param on_log:
153+
:return:
154+
"""
155+
self.__client.on_log = on_log
156+
157+
def set_on_message_callback(self, sub, on_message_callback):
158+
"""
159+
Set the on_message callback for a specific topic.
160+
:param sub:
161+
:param on_message_callback:
162+
:return:
163+
"""
164+
self.__client.message_callback_add(sub, on_message_callback)
165+
166+
def start(self):
167+
"""
168+
Start the MQTT client in a separate thread. This is required for the client to be able to receive messages.
169+
170+
:return:
171+
"""
172+
self.__client.loop_start()
173+
174+
def stop(self):
175+
"""
176+
Stop the MQTT client.\
177+
178+
:return:
179+
"""
180+
self.__client.loop_stop()
181+
182+
def __toggle_is_connected(self):
183+
self.__is_connected = not self.__is_connected
184+
185+
def is_connected(self):
186+
return self.__is_connected
187+
188+
@staticmethod
189+
def publish_single(self, topic, msg):
190+
self.__client.single(topic, msg, 0)
191+
192+
@staticmethod
193+
def publish_multiple(self, topic, msgs):
194+
self.__client.multiple(msgs, )
195+
196+
def tls_set(self):
197+
self.__client.tls_set()

oshconnect/csapi4py/con_sys_api.py

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
from typing import Union
2+
3+
from pydantic import BaseModel, HttpUrl, Field
4+
5+
from oshconnect.csapi4py.endpoints import Endpoint
6+
from oshconnect.csapi4py.request_wrappers import post_request, put_request, get_request, delete_request
7+
8+
9+
class ConnectedSystemAPIRequest(BaseModel):
10+
url: HttpUrl = Field(None)
11+
body: Union[dict, str] = Field(None)
12+
params: dict = Field(None)
13+
request_method: str = Field('GET')
14+
headers: dict = Field(None)
15+
auth: Union[tuple, None] = Field(None)
16+
17+
def make_request(self):
18+
match self.request_method:
19+
case 'GET':
20+
return get_request(self.url, self.params, self.headers, self.auth)
21+
case 'POST':
22+
print(f'POST request: {self}')
23+
return post_request(self.url, self.body, self.headers, self.auth)
24+
case 'PUT':
25+
print(f'PUT request: {self}')
26+
return put_request(self.url, self.body, self.headers, self.auth)
27+
case 'DELETE':
28+
print(f'DELETE request: {self}')
29+
return delete_request(self.url, self.params, self.headers, self.auth)
30+
case _:
31+
raise ValueError('Invalid request method')
32+
33+
34+
class ConnectedSystemsRequestBuilder(BaseModel):
35+
api_request: ConnectedSystemAPIRequest = Field(default_factory=ConnectedSystemAPIRequest)
36+
base_url: HttpUrl = None
37+
endpoint: Endpoint = Field(default_factory=Endpoint)
38+
39+
def with_api_url(self, url: HttpUrl):
40+
self.api_request.url = url
41+
return self
42+
43+
def with_server_url(self, server_url: HttpUrl):
44+
self.base_url = server_url
45+
return self
46+
47+
def build_url_from_base(self):
48+
"""
49+
Builds the full API endpoint URL from the base URL and the endpoint parameters that have been previously
50+
provided.
51+
"""
52+
self.api_request.url = f'{self.base_url}/{self.endpoint.create_endpoint()}'
53+
return self
54+
55+
def with_api_root(self, api_root: str):
56+
"""
57+
Optional: Set the API root for the request. This is useful if you want to use a different API root than the
58+
default one (api).
59+
:param api_root:
60+
:return:
61+
"""
62+
self.endpoint.api_root = api_root
63+
return self
64+
65+
def for_resource_type(self, resource_type: str):
66+
self.endpoint.base_resource = resource_type
67+
return self
68+
69+
def with_resource_id(self, resource_id: str):
70+
self.endpoint.resource_id = resource_id
71+
return self
72+
73+
def for_sub_resource_type(self, sub_resource_type: str):
74+
self.endpoint.sub_component = sub_resource_type
75+
return self
76+
77+
def with_secondary_resource_id(self, resource_id: str):
78+
self.endpoint.secondary_resource_id = resource_id
79+
return self
80+
81+
def with_request_body(self, request_body: str):
82+
self.api_request.body = request_body
83+
return self
84+
85+
def with_request_method(self, request_method: str):
86+
self.api_request.request_method = request_method
87+
return self
88+
89+
def with_headers(self, headers: dict = None):
90+
# TODO: ensure headers can default if excluded
91+
self.api_request.headers = headers
92+
return self
93+
94+
def with_auth(self, uname: str, pword: str):
95+
self.api_request.auth = (uname, pword)
96+
return self
97+
98+
def build(self):
99+
# convert endpoint to HttpUrl
100+
return self.api_request
101+
102+
def reset(self):
103+
self.api_request = ConnectedSystemAPIRequest()
104+
self.endpoint = Endpoint()
105+
return self

0 commit comments

Comments
 (0)