Skip to content

Commit cb3810f

Browse files
committed
Fix: Call sendAll in example_remode_nodes since not using Realtime subclass. Separate RealtimePhysicalLayer from RealtimeRawPhysicalLayer for clarity.
1 parent 6bffdad commit cb3810f

16 files changed

+154
-28
lines changed

examples/example_message_interface.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ def printMessage(msg):
9191
while True:
9292
count = 0
9393
count += physicalLayer.sendAll(sock, verbose=True)
94-
count += physicalLayer.receiveAll(sock, verbose=settings['trace'])
94+
count += physicalLayer.receiveAll(sock, verbose=True)
9595
if count < 1:
9696
precise_sleep(.01)
9797
# else skip sleep to avoid latency (port already delayed)

examples/example_remote_nodes.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -199,7 +199,7 @@ def result(arg1, arg2=None, arg3=None, result=True) :
199199
NodeID(settings['localNodeID']), None)
200200
if settings['trace'] : print("SM: {}".format(message))
201201
canLink.sendMessage(message)
202-
202+
physicalLayer.sendAll(sock)
203203
# pull the received messages
204204
while True :
205205
try :
@@ -218,4 +218,6 @@ def result(arg1, arg2=None, arg3=None, result=True) :
218218

219219
# this ends here, which takes the local node offline
220220

221+
# For explicitness (to make this example match use in non-linear
222+
# application), notify openlcb of disconnect:
221223
physicalLayer.onDisconnect()

examples/example_tcp_message_interface.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
# region same code as other examples
1616
from examples_settings import Settings # do 1st to fix path if no pip install
1717
from openlcb import precise_sleep
18-
from openlcb.realtimephysicallayer import RealtimePhysicalLayer
18+
from openlcb.realtimerawphysicallayer import RealtimeRawPhysicalLayer
1919
settings = Settings()
2020

2121
if __name__ == "__main__":
@@ -55,14 +55,14 @@
5555
# assert isinstance(data, (bytes, bytearray))
5656
# print(" SR: {}".format(data))
5757
# sock.send(data)
58-
# ^ Moved to RealtimePhysicalLayer sendFrameAfter override
58+
# ^ Moved to RealtimeRawPhysicalLayer sendFrameAfter override
5959

6060

6161
def printMessage(msg):
6262
print("RM: {} from {}".format(msg, msg.source))
6363

6464

65-
physicalLayer = RealtimePhysicalLayer(sock)
65+
physicalLayer = RealtimeRawPhysicalLayer(sock)
6666
# ^ this was not in the example before
6767
# (just gave sendToSocket to TcpLink)
6868

@@ -80,8 +80,8 @@ def printMessage(msg):
8080
message = Message(MTI.Verify_NodeID_Number_Global,
8181
NodeID(settings['localNodeID']), None)
8282
print("SM: {}".format(message))
83-
tcpLinkLayer.sendMessage(message)
84-
83+
tcpLinkLayer.sendMessage(message, verbose=True)
84+
physicalLayer.sendAll(sock, verbose=True) # only a formality since Realtime
8585
# N/A
8686
# while not tcpLinkLayer.getState() == TcpLink.State.Permitted:
8787
# time.sleep(.02)

openlcb/canbus/canlink.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -726,7 +726,7 @@ def handleReceivedData(self, frame: CanFrame):
726726
msg.originalMTI = ((frame.header >> 12) & 0xFFF)
727727
self.fireMessageReceived(msg)
728728

729-
def sendMessage(self, msg: Message):
729+
def sendMessage(self, msg: Message, verbose=False):
730730
# special case for datagram
731731
if msg.mti == MTI.Datagram:
732732
header = 0x10_00_00_00

openlcb/canbus/canphysicallayergridconnect.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -214,4 +214,4 @@ def handleData(self, data: Union[bytes, bytearray], verbose=False) -> int:
214214

215215
# shorten buffer by removing the processed message
216216
self.inboundBuffer = self.inboundBuffer[lastByte:]
217-
return frameCount
217+
return frameCount

openlcb/canbus/canphysicallayersimulation.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
Simulated CanPhysicalLayer to record frames requested to be sent.
33
'''
44

5-
from typing import List
5+
from typing import List, Union
66
from openlcb.canbus.canframe import CanFrame
77
from openlcb.canbus.canphysicallayer import CanPhysicalLayer
88
from openlcb.frameencoder import FrameEncoder
@@ -25,9 +25,11 @@ def __init__(self):
2525
def _onQueuedFrame(self, frame: CanFrame):
2626
raise AttributeError("Not implemented for simulation")
2727

28-
def handleData(self, data: bytearray, verbose=False):
28+
def handleData(self, data: Union[bytes, bytearray], verbose=False) -> int:
2929
# Do not parse, since simulation. Just collect for later analysis
3030
self.received_chunks.append(data)
31+
frameCount = 1 # assumed for simulation
32+
return frameCount
3133

3234
def encodeFrameAsString(self, frame: CanFrame):
3335
return "(no encoding, only simulating CanPhysicalLayer superclass)"

openlcb/linklayer.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,7 @@ def _onStateChanged(self, oldState, newState):
151151
raise NotImplementedError(
152152
"[LinkLayer] abstract _onStateChanged not implemented")
153153

154-
def sendMessage(self, msg: Message):
154+
def sendMessage(self, msg: Message, verbose=False):
155155
'''This is the basic abstract interface
156156
'''
157157

openlcb/physicallayer.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,8 @@ def hasFrame(self) -> bool:
7373
"""Check if there is a frame queued to send."""
7474
return bool(self._send_frames)
7575

76-
def sendAll(self, device: PortInterface, mode="binary", verbose=False) -> int:
76+
def sendAll(self, device: PortInterface, mode="binary",
77+
verbose=False) -> int:
7778
"""Abstract method for sending queued frames"""
7879
raise NotImplementedError(
7980
"This must be implemented in a subclass"

openlcb/rawphysicallayer.py

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
from typing import Union
2+
from openlcb.frameencoder import FrameEncoder
3+
from openlcb.physicallayer import PhysicalLayer
4+
5+
6+
class RawPhysicalLayer(PhysicalLayer, FrameEncoder):
7+
"""Implements FrameEncoder but leaves PhysicalLayer untouched
8+
so that PhysicalLayer methods can be implemented by subclass or
9+
sibling class used as second superclass by subclass.
10+
- This FrameEncoder implementation doesn't actually
11+
encode CanFrame--only converts between str & bytes!
12+
"""
13+
def __init__(self, *args, **kwargs):
14+
PhysicalLayer.__init__(self, *args, **kwargs)
15+
FrameEncoder.__init__(self, *args, **kwargs)
16+
17+
def encodeFrameAsString(self, frame) -> str:
18+
if isinstance(frame, str):
19+
return frame
20+
elif isinstance(frame, (bytearray, bytes)):
21+
return frame.decode("utf-8")
22+
raise TypeError(
23+
"Only str, bytes, or bytearray is allowed for RawPhysicalLayer."
24+
" For {} use/make another Encoder implementation."
25+
.format(type(self).__name__))
26+
27+
def encodeFrameAsData(self, frame) -> Union[bytearray, bytes]:
28+
if isinstance(frame, str):
29+
return frame.encode("utf-8")
30+
elif isinstance(frame, (bytearray, bytes)):
31+
return frame
32+
raise TypeError(
33+
"Only str, bytes, or bytearray is allowed for RawPhysicalLayer."
34+
" For frame type {} use/make another FrameEncoder implementation."
35+
.format(type(self).__name__))

openlcb/realtimephysicallayer.py

Lines changed: 57 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,23 @@
11

2-
from enum import Enum
32
from logging import getLogger
43
from typing import Union
54

65
from openlcb.physicallayer import PhysicalLayer
6+
from openlcb.portinterface import PortInterface
77

88
logger = getLogger(__name__)
99

1010

1111
class RealtimePhysicalLayer(PhysicalLayer):
12-
12+
"""A realtime physical layer is only for use when there is an
13+
absence of a link layer (or link layer doesn't enqueue frames) *and*
14+
the application is not multi-threaded or uses a lock and avoids
15+
race conditions.
16+
Otherwise, overlapping port calls (*undefined behavior* at OS level)
17+
may occur!
18+
TODO: Add a lock variable and do reads here so all port usage can
19+
utilize the lock and prevent overlapping use of the port.
20+
"""
1321
class State:
1422
Initial = 0
1523
Disconnected = 1
@@ -18,34 +26,74 @@ class State:
1826
DisconnectedState = State.Disconnected
1927

2028
def __init__(self, socket):
29+
PhysicalLayer.__init__(self)
2130
# sock to distinguish from socket module or socket.socket class!
2231
self.sock = socket
2332

24-
def sendDataAfter(self, data: Union[bytearray, bytes]):
33+
def sendDataAfter(self, data: Union[bytearray, bytes], verbose=True):
34+
"""Send data (immediately, since realtime subclass).
35+
36+
Args:
37+
data (Union[bytearray, bytes, CanFrame]): data to send.
38+
verbose (bool, optional): verbose is only for Realtime
39+
subclass (since data is sent immediately), otherwise set
40+
verbose on sendAll. Defaults to False.
41+
"""
2542
# if isinstance(data, list):
2643
# raise TypeError(
2744
# "Got {}({}) but expected str"
2845
# .format(type(data).__name__, data)
2946
# )
3047
assert isinstance(data, (bytes, bytearray))
31-
print(" SR: {}".format(data))
48+
if verbose:
49+
print(" SR: {}".format(data))
3250
self.sock.send(data)
3351

34-
def sendFrameAfter(self, frame):
52+
def sendFrameAfter(self, frame, verbose=False):
53+
"""Send frame (immediately, since realtime subclass).
54+
55+
Args:
56+
data (Union[bytearray, bytes, CanFrame]): data to send.
57+
verbose (bool, optional): verbose is only for Realtime
58+
subclass (since data is sent immediately), otherwise set
59+
verbose on sendAll. Defaults to False.
60+
"""
61+
if hasattr(self, 'encodeFrameAsData'):
62+
data = self.encodeFrameAsData(frame)
63+
else:
64+
assert isinstance(frame, (bytes, bytearray, str)), \
65+
"Use a FrameEncoder implementation if not using bytes/str"
66+
if isinstance(frame, str):
67+
data = frame.encode("utf-8")
68+
else:
69+
data = frame
3570
# if isinstance(data, list):
3671
# raise TypeError(
3772
# "Got {}({}) but expected str"
3873
# .format(type(data).__name__, data)
3974
# )
40-
print(" SR: {}".format(frame.encode()))
75+
if verbose:
76+
print(" SR: {}".format(frame))
4177
# send and fireFrameReceived would usually occur after
4278
# frame from _send_frames.popleft is sent,
4379
# but we do all this here in the Realtime subclass:
44-
self.sock.send(frame.encode())
45-
# TODO: finish onFrameSent
46-
if frame.afterSendState:
80+
self.sock.send(data)
81+
if hasattr(frame, 'afterSendState') and frame.afterSendState:
82+
# Use hasattr since only applicable to subclasses that use
83+
# CanFrame.
4784
self.fireFrameReceived(frame) # also calls self.onFrameSent(frame)
4885

86+
def sendAll(self, device: PortInterface, mode="binary",
87+
verbose=False) -> int:
88+
"""sendAll is only a stub in the case of realtime subclasses.
89+
Instead of popping frames it performs a check to ensure the
90+
queue is not used (since queue should only be used for typical
91+
subclasses which are queued).
92+
"""
93+
if len(self._send_frames) > 0:
94+
raise AssertionError("Realtime subclasses should not use a queue!")
95+
logger.debug("sendAll ran (realtime subclass, so nothing to do)")
96+
4997
def registerFrameReceivedListener(self, listener):
5098
"""Register a new frame received listener
5199
(optional since LinkLayer subclass constructor sets

0 commit comments

Comments
 (0)