-
Notifications
You must be signed in to change notification settings - Fork 0
/
iscpc
executable file
·171 lines (158 loc) · 5.44 KB
/
iscpc
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
#!/usr/bin/env python
#
# Integra Serial Communication Protocol Client
#
from __future__ import print_function
import argparse
import socket
import select
import struct
import sys
import signal
import os
import errno
import time
class Message(object):
@classmethod
def ethernet_packet_scan(cls, string):
index = string.find('ISCP')
if index == -1:
return None, string
string = string[index:]
if len(string) < 12:
return None, string
banner = string[0:4]
header_size = struct.unpack('!i', string[4:8])[0]
data_size = struct.unpack('!i', string[8:12])[0]
if len(string) < header_size + data_size:
return None, string
version = ord(string[12])
reserved = string[13:header_size]
data = string[header_size:header_size+data_size]
string = string[header_size+data_size:]
unit = data[1]
message = data[2:].rstrip(chr(0x0a)+chr(0x0d)+chr(0x1a))
return cls(message[0:3], message[3:]), string
def __init__(self, command, parameter):
self.command = command.encode('ascii')[0:3]
self.parameter = parameter.encode('ascii')
def packet(self):
data = '!' + '1' + self.command + self.parameter + chr(0x0a)
header = 'ISCP' + \
struct.pack('!i', 16) + \
struct.pack('!i', len(data)) + \
chr(1) + \
chr(0)+chr(0)+chr(0)
return bytearray(header+data, 'ascii')
def __str__(self):
return self.command + self.parameter
def __repr__(self):
return '{ISCP %s %s}' % (self.command, self.parameter)
class Pipe(object):
def __init__(self):
(self.read, self.write) = os.pipe()
class Alarms(object):
def __init__(self):
self._alarms = {}
def add(self, time, action):
self[float(time)] = action
def timeout(self):
if self._alarms:
return self._alarms.keys()[0] - time.time()
else:
return None
def do(self):
for t,action in self._alarms:
if t <= time.time():
action()
else:
break
class ISCPClient(object):
def __init__(self):
parser = argparse.ArgumentParser(description='Talk to an ISCP server on standard input.')
parser.add_argument('host', help='Hostname or address of ISCP server')
parser.add_argument('port', nargs='?', default=60128, type=int,
help='ISCP server port (default is 60128)')
parser.add_argument('-e', '--exit', action='append',
help='Exit on given type of server response')
parser.add_argument('-w', '--wait', default=-1, type=float,
help='Wait given seconds before exitting after last message. Negative value means wait forever (default)')
parser.add_argument('-i', '--interval', default=0.05, type=float,
help='Minimum interval between messages to server in seconds (default is 0.05)')
args = parser.parse_args(namespace=self)
self.signals = []
self.pipe = None
self.socket = None
self.msg_queue = []
self.rlist = [sys.stdin]
self.last_msg_time = time.time() - self.interval
def _connect(self):
self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
try:
self.socket.connect((self.host, self.port))
except socket.error as e:
print('Connection failed: %s' % e.strerror, file=sys.stderr)
sys.exit(e.errno)
self.rlist.append(self.socket)
def _catch_signals(self):
self.pipe = Pipe()
signal.set_wakeup_fd(self.pipe.write)
signal.signal(signal.SIGINT, lambda s,f: self.signals.append(s))
signal.signal(signal.SIGPIPE, lambda s,f: self.signals.append(s))
self.rlist.append(self.pipe.read)
def _get_timeout(self):
if self.msg_queue:
return self.interval - (time.time() - self.last_msg_time)
elif sys.stdin not in self.rlist and self.wait >= 0:
return (self.last_msg_time + self.wait) - time.time()
else:
return None
def run(self):
self._connect()
self._catch_signals()
buf = ''
while self.socket in self.rlist and \
not self.signals and \
not (not self.msg_queue and self.wait >= 0 and (time.time() - self.last_msg_time) >= self.wait):
try:
rsel,_,_ = select.select(self.rlist, (), (), self._get_timeout())
except select.error as err:
if err[0] == errno.EINTR:
# Probably raised due to signal handling
continue
else:
raise
# Handle messages from server
if self.socket in rsel:
new = self.socket.recv(1024)
if new:
buf += new
message,buf = Message.ethernet_packet_scan(buf)
if message is not None:
print(str(message))
if self.exit and message.command in self.exit:
self.rlist.remove(self.socket)
try:
sys.stdout.flush()
except IOError as e:
print(e.strerror, file=sys.stderr)
sys.exit(e.errno)
pass
else:
self.rlist.remove(self.socket)
# Queue up messages from client
if sys.stdin in rsel:
cmd = sys.stdin.readline()
if cmd:
cmd = cmd.strip()
self.msg_queue.append(Message(cmd[0:3], cmd[3:]))
else:
self.rlist.remove(sys.stdin)
# Send queued messages to server
if self.msg_queue and (time.time() - self.last_msg_time) > self.interval:
self.socket.sendall(self.msg_queue.pop(0).packet())
self.last_msg_time = time.time()
def main():
ISCPClient().run()
if __name__ == "__main__":
main()