1
|
|
|
'''# Full Cycle Mining Messages''' |
2
|
|
|
import json |
3
|
|
|
from marshmallow import Schema, fields, post_load |
4
|
|
|
from messaging.schema import MinerCommandSchema, MinerSchema, MinerStatsSchema, MinerCurrentPoolSchema |
5
|
|
|
from domain.mining import Miner, MinerCommand |
6
|
|
|
|
7
|
|
|
class MessageSchema(Schema): |
8
|
|
|
'''schema for message''' |
9
|
|
|
timestamp = fields.DateTime() |
10
|
|
|
sender = fields.Str() |
11
|
|
|
type = fields.Str() |
12
|
|
|
body = fields.Str() |
13
|
|
|
version = fields.Str() |
14
|
|
|
|
15
|
|
|
#def make_user(self, data): |
16
|
|
|
# return User(**data) |
17
|
|
|
|
18
|
|
|
@post_load |
19
|
|
|
def make_message(self, data): |
20
|
|
|
'''reconstitute a message''' |
21
|
|
|
msg = Message(data['timestamp'], data['sender'], data['type'], data['body']) |
22
|
|
|
return msg |
23
|
|
|
|
24
|
|
|
class MinerCommandMessage(object): |
25
|
|
|
"""Command that could be sent to a miner""" |
26
|
|
|
def __init__(self, command='', parameter=''): |
27
|
|
|
self.command = command |
28
|
|
|
self.parameter = parameter |
29
|
|
|
|
30
|
|
|
class MinerMessage(object): |
31
|
|
|
""" Miner information for a message |
32
|
|
|
Miner could be None, meaning message is for all |
33
|
|
|
command could be None, meaning message is informational for a specific miner |
34
|
|
|
""" |
35
|
|
|
def __init__(self, miner, command=None, minerstats=None, minerpool=None): |
36
|
|
|
self.command = command |
37
|
|
|
self.miner = miner |
38
|
|
|
self.minerstats = minerstats |
39
|
|
|
self.minerpool = minerpool |
40
|
|
|
|
41
|
|
|
class MinerMessageSchema(Schema): |
42
|
|
|
'''schema for minermessage''' |
43
|
|
|
command = fields.Nested(MinerCommandSchema) |
44
|
|
|
miner = fields.Nested(MinerSchema) |
45
|
|
|
minerstats = fields.Nested(MinerStatsSchema) |
46
|
|
|
minerpool = fields.Nested(MinerCurrentPoolSchema) |
47
|
|
|
|
48
|
|
|
@post_load |
49
|
|
|
def make_minermessage(self, data): |
50
|
|
|
'''reconstitute a minermessage''' |
51
|
|
|
miner = None |
52
|
|
|
command = None |
53
|
|
|
minerstats = None |
54
|
|
|
minerpool = None |
55
|
|
|
if 'miner' in data: |
56
|
|
|
#miner comes in as dict instead of entity when there is an error in the schema |
57
|
|
|
if isinstance(data['miner'], Miner): |
58
|
|
|
miner = data['miner'] |
59
|
|
|
else: |
60
|
|
|
miner = Miner(**data['miner']) |
61
|
|
|
if 'command' in data: |
62
|
|
|
if isinstance(data['command'], MinerCommand): |
63
|
|
|
command = data['command'] |
64
|
|
|
else: |
65
|
|
|
command = MinerCommand(**data['command']) |
66
|
|
|
if 'minerstats' in data: |
67
|
|
|
#minerstats = MinerStatistics(Miner=miner,**data['minerstats']) |
68
|
|
|
minerstats = data['minerstats'] |
69
|
|
|
if 'minerpool' in data: |
70
|
|
|
#minerpool = MinerCurrentPool(Miner=miner, **data['minerpool']) |
71
|
|
|
minerpool = data['minerpool'] |
72
|
|
|
entity = MinerMessage(miner, command, minerstats, minerpool) |
73
|
|
|
return entity |
74
|
|
|
|
75
|
|
|
|
76
|
|
|
class Message(object): |
77
|
|
|
"""A Full Cycle Mining Message |
78
|
|
|
Keep generic so that body can be almost anything |
79
|
|
|
A generic message envelope |
80
|
|
|
See http://marshmallow.readthedocs.io/en/latest/extending.html |
81
|
|
|
""" |
82
|
|
|
def __init__(self, timestamp, sender='', message_type='', body='', version=1.1): |
83
|
|
|
self.version = version |
84
|
|
|
self.timestamp = timestamp |
85
|
|
|
self.sender = sender |
86
|
|
|
self.type = message_type |
87
|
|
|
self.body = body |
88
|
|
|
|
89
|
|
|
def jsonserialize(self, sch, msg): |
90
|
|
|
'''json serialize a message''' |
91
|
|
|
return json.dumps(sch.dump(msg)) |
92
|
|
|
|
93
|
|
|
def bodyjson(self): |
94
|
|
|
'''for some reason it gets encoded as list. just return first element''' |
95
|
|
|
#todo: test why python serialization makes it a list but node does not |
96
|
|
|
jsonlist = json.loads(self.body) |
97
|
|
|
if 'miner' in jsonlist: |
98
|
|
|
return jsonlist |
99
|
|
|
if not isinstance(jsonlist, list): |
100
|
|
|
return jsonlist |
101
|
|
|
return jsonlist[0] |
102
|
|
|
|
103
|
|
|
def bodyonlyjson(self): |
104
|
|
|
'''deserialize body of message''' |
105
|
|
|
jsononly = json.loads(self.body) |
106
|
|
|
return jsononly |
107
|
|
|
|
108
|
|
|
def make_any(self, message_type, body): |
109
|
|
|
'''make a message''' |
110
|
|
|
self.type = message_type |
111
|
|
|
self.body = body |
112
|
|
|
return self |
113
|
|
|
|
114
|
|
|
def make_minerstats(self, miner, minerstats=None, minerpool=None): |
115
|
|
|
'''make a miner stats message''' |
116
|
|
|
self.type = 'minerstats' |
117
|
|
|
minermsg = MinerMessage(miner, command=None, minerstats=minerstats, minerpool=minerpool) |
118
|
|
|
self.body = self.jsonserialize(MinerMessageSchema(), minermsg) |
119
|
|
|
return self |
120
|
|
|
|
121
|
|
|
def make_command(self, command): |
122
|
|
|
'''make a command message''' |
123
|
|
|
self.type = 'command' |
124
|
|
|
msg = MinerCommandMessage(command.command, command.parameter) |
125
|
|
|
self.body = self.jsonserialize(MinerCommandSchema(), msg) |
126
|
|
|
return self |
127
|
|
|
|
128
|
|
|
def make_minercommand(self, miner, command): |
129
|
|
|
'''make a miner commad message''' |
130
|
|
|
self.type = 'minercommand' |
131
|
|
|
minermsg = MinerMessage(miner, command=command) |
132
|
|
|
self.body = self.jsonserialize(MinerMessageSchema(), minermsg) |
133
|
|
|
return self |
134
|
|
|
|
135
|
|
|
#these will handled independently |
136
|
|
|
#def make_sensorvalue(self, sensorvalue): |
137
|
|
|
# self.type = 'sensorvalue' |
138
|
|
|
# msg = SensorValueMessage(sensor.sensorid, sensor.value) |
139
|
|
|
# self.body = self.jsonserialize(SensorValueSchema(),msg) |
140
|
|
|
# return self |
141
|
|
|
|
142
|
|
|
#def make_sensor(self, sensor): |
143
|
|
|
# self.type = 'sensor' |
144
|
|
|
# msg = SensorMessage(sensor.sensorid, sensor.value) |
145
|
|
|
# self.body = self.jsonserialize(SensorSchema(),msg) |
146
|
|
|
# return self |
147
|
|
|
|
148
|
|
|
class ConfigurationMessage(object): |
149
|
|
|
""" A configuration message |
150
|
|
|
Used to save full cycle configuration values |
151
|
|
|
example: command = 'save', entity='pool', id={"poolid":"1"}, values = [{"name":"my pool"}] |
152
|
|
|
""" |
153
|
|
|
def __init__(self, command=None, parameter=None, entity=None, configuration_message_id=None, values=None): |
154
|
|
|
self.command = command |
155
|
|
|
self.parameter = parameter |
156
|
|
|
self.entity = entity |
157
|
|
|
self.configuration_message_id = configuration_message_id |
158
|
|
|
self.values = values |
159
|
|
|
|
160
|
|
|
class ConfigurationMessageSchema(Schema): |
161
|
|
|
'''schema for Configuration Message''' |
162
|
|
|
command = fields.Str() |
163
|
|
|
parameter = fields.Str() |
164
|
|
|
entity = fields.Str() |
165
|
|
|
configuration_message_id = fields.Dict() |
166
|
|
|
values = fields.List(fields.Dict()) |
167
|
|
|
|
168
|
|
|
@post_load |
169
|
|
|
def make_configurationmessage(self, data): |
170
|
|
|
'''reconstitute a configurationmessage''' |
171
|
|
|
if isinstance(data, ConfigurationMessage): |
172
|
|
|
return data |
173
|
|
|
command = None |
174
|
|
|
parameter = None |
175
|
|
|
entity = None |
176
|
|
|
configuration_message_id = None |
177
|
|
|
values = None |
178
|
|
|
command = data['command'] |
179
|
|
|
if 'parameter' in data: |
180
|
|
|
parameter = data['parameter'] |
181
|
|
|
entity = data['entity'] |
182
|
|
|
if 'configuration_message_id' in data: |
183
|
|
|
configuration_message_id = data['configuration_message_id'] |
184
|
|
|
if 'values' in data: |
185
|
|
|
values = data['values'] |
186
|
|
|
msg = ConfigurationMessage(command, parameter, entity, configuration_message_id, values) |
187
|
|
|
return msg |
188
|
|
|
|