def add(self, clntsock, clntaddr):
fd = clntsock.fileno()
+ log.info("Start serving fd %d from %s", fd, clntaddr)
self.by_fd[fd] = Client(clntsock, clntaddr)
return fd
if proto_of_message(packet) == LOGIN.PROTO: # Could do blindly...
self.by_imei[clnt.imei] = clnt
result.append((clnt.imei, when, peeraddr, packet))
+ log.debug(
+ "Received from %s (IMEI %s): %s",
+ peeraddr,
+ clnt.imei,
+ packet.hex(),
+ )
return result
def response(self, resp):
"make_response",
"parse_message",
"proto_by_name",
+ "Dir",
"GPS303Pkt",
"UNKNOWN",
"LOGIN",
class GPS303Pkt:
PROTO: int
- DIR = Dir.INLINE # Most packets anticipate simple acknowledgement
+ DIR = Dir.IN # Do not send anything back by default
def __init__(self, *args, **kwargs):
assert len(args) == 0
class UNKNOWN(GPS303Pkt):
PROTO = 256 # > 255 is impossible in real packets
- DIR = Dir.IN
class LOGIN(GPS303Pkt):
PROTO = 0x01
+ DIR = Dir.INLINE
# Default response for ACK, can also respond with STOP_UPLOAD
@classmethod
class HEARTBEAT(GPS303Pkt):
PROTO = 0x08
+ DIR = Dir.INLINE
class _GPS_POSITIONING(GPS303Pkt):
+ DIR = Dir.INLINE
+
@classmethod
def from_packet(cls, length, payload):
self = super().from_packet(length, payload)
class HIBERNATION(GPS303Pkt):
PROTO = 0x14
- DIR = Dir.EXT
+ DIR = Dir.INLINE
@classmethod
def response(cls): # Server can send to send devicee to sleep
class RESET(GPS303Pkt): # Device sends when it got reset SMS
PROTO = 0x15
- DIR = Dir.EXT
@classmethod
def response(cls): # Server can send to initiate factory reset
class WIFI_OFFLINE_POSITIONING(_WIFI_POSITIONING):
PROTO = 0x17
+ DIR = Dir.INLINE
@classmethod
def inline_response(cls, packet):
class TIME(GPS303Pkt):
PROTO = 0x30
+ DIR = Dir.INLINE
@classmethod
def inline_response(cls, packet):
def from_packet(cls, length, payload):
self = super().from_packet(length, payload)
self.flag = payload[0]
+ return self
class SETUP(GPS303Pkt):
class MANUAL_POSITIONING(GPS303Pkt):
PROTO = 0x80
- DIR = Dir.EXT
+ DIR = Dir.OUT
@classmethod
def from_packet(cls, length, payload):
self = super().from_packet(length, payload)
- self.flag = payload[0]
+ self.flag = payload[0] if len(payload) > 0 else None
self.reason = {
1: "Incorrect time",
2: "LBS less",
6: "LBS prohibited, WiFi absent",
7: "GPS spacing < 50 m",
}.get(self.flag, "Unknown")
+ return self
@classmethod
def response(cls):
def class_by_prefix(prefix):
- lst = [(name, proto) for name, proto in PROTOS.items()
- if name.upper().startswith(prefix.upper())]
+ lst = [
+ (name, proto)
+ for name, proto in PROTOS.items()
+ if name.upper().startswith(prefix.upper())
+ ]
if len(lst) != 1:
return lst
_, proto = lst[0]
zsub = zctx.socket(zmq.SUB)
zsub.connect(conf.get("collector", "publishurl"))
for protoname in (
- "SUPERVISION",
"STATUS",
- "RESET",
- "WHITELIST_TOTAL",
- "PROHIBIT_LBS",
"SETUP",
"POSITION_UPLOAD_INTERVAL",
):
datetime.fromtimestamp(zmsg.when).astimezone(tz=timezone.utc),
msg,
)
+ if msg.DIR is not Dir.EXT:
+ log.error(
+ "%s does not expect externally provided response", msg
+ )
kwargs = {}
if isinstance(msg, STATUS):
kwargs = {