forked from wirenboard/wb-mqtt-serial
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmercury230_device.cpp
165 lines (145 loc) · 5.72 KB
/
mercury230_device.cpp
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
#include <cassert>
#include <iostream>
#include "mercury230_device.h"
#include "crc16.h"
REGISTER_BASIC_INT_PROTOCOL("mercury230", TMercury230Device, TRegisterTypes({
{ TMercury230Device::REG_VALUE_ARRAY, "array", "power_consumption", U32, true },
{ TMercury230Device::REG_VALUE_ARRAY12, "array12", "power_consumption", U32, true },
{ TMercury230Device::REG_PARAM, "param", "value", U24, true },
{ TMercury230Device::REG_PARAM_SIGN_ACT, "param_sign_active", "value", S24, true },
{ TMercury230Device::REG_PARAM_SIGN_REACT, "param_sign_reactive", "value", S24, true },
{ TMercury230Device::REG_PARAM_SIGN_IGNORE, "param_sign_ignore", "value", U24, true },
{ TMercury230Device::REG_PARAM_BE, "param_be", "value", S24, true }
}));
TMercury230Device::TMercury230Device(PDeviceConfig device_config, PPort port, PProtocol protocol)
: TEMDevice<TBasicProtocol<TMercury230Device>>(device_config, port, protocol)
{}
bool TMercury230Device::ConnectionSetup( )
{
uint8_t setupCmd[7] = {
uint8_t(DeviceConfig()->AccessLevel), 0x01, 0x01, 0x01, 0x01, 0x01, 0x01
};
std::vector<uint8_t> password = DeviceConfig()->Password;
if (password.size()) {
if (password.size() != 6)
throw TSerialDeviceException("invalid password size (6 bytes expected)");
std::copy(password.begin(), password.end(), setupCmd + 1);
}
uint8_t buf[1];
WriteCommand(0x01, setupCmd, 7);
try {
return ReadResponse(0x00, buf, 0);
} catch (TSerialDeviceTransientErrorException&) {
// retry upon response from a wrong slave
return false;
} catch (TSerialDevicePermanentRegisterException&) {
return false;
}
}
TEMDevice<TMercury230Protocol>::ErrorType TMercury230Device::CheckForException(uint8_t* frame, int len, const char** message)
{
*message = 0;
if (len != 4 || (frame[1] & 0x0f) == 0)
return TEMDevice<TMercury230Protocol>::NO_ERROR;
switch (frame[1] & 0x0f) {
case 1:
*message = "Invalid command or parameter";
return TEMDevice<TMercury230Protocol>::PERMANENT_ERROR;
case 2:
*message = "Internal meter error";
break;
case 3:
*message = "Insufficient access level";
break;
case 4:
*message = "Can't correct the clock more than once per day";
break;
case 5:
*message = "Connection closed";
return TEMDevice<TMercury230Protocol>::NO_OPEN_SESSION;
default:
*message = "Unknown error";
}
return TEMDevice<TMercury230Protocol>::OTHER_ERROR;
}
const TMercury230Device::TValueArray& TMercury230Device::ReadValueArray(uint32_t address, int resp_len)
{
int key = (address >> 4) | (SlaveId << 24);
auto it = CachedValues.find(key);
if (it != CachedValues.end())
return it->second;
uint8_t cmdBuf[2];
cmdBuf[0] = (uint8_t)((address >> 4) & 0xff); // high nibble = array number, lower nibble = month
cmdBuf[1] = (uint8_t)((address >> 12) & 0x0f); // tariff
uint8_t buf[MAX_LEN], *p = buf;
TValueArray a;
Talk(0x05, cmdBuf, 2, -1, buf, resp_len * 4);
for (int i = 0; i < resp_len; i++, p += 4) {
a.values[i] = ((uint32_t)p[1] << 24) +
((uint32_t)p[0] << 16) +
((uint32_t)p[3] << 8 ) +
(uint32_t)p[2];
}
return CachedValues.insert(std::make_pair(key, a)).first->second;
}
uint32_t TMercury230Device::ReadParam( uint32_t address, unsigned resp_payload_len, RegisterType reg_type)
{
uint8_t cmdBuf[2];
cmdBuf[0] = (address >> 8) & 0xff; // param
cmdBuf[1] = address & 0xff; // subparam (BWRI)
assert(resp_payload_len <= 3);
uint8_t buf[3] = {};
Talk( 0x08, cmdBuf, 2, -1, buf, resp_payload_len);
if (resp_payload_len == 3) {
if ((reg_type == REG_PARAM_SIGN_ACT) || (reg_type == REG_PARAM_SIGN_REACT) || (reg_type == REG_PARAM_SIGN_IGNORE)) {
uint32_t magnitude = (((uint32_t)buf[0] & 0x3f) << 16) +
((uint32_t)buf[2] << 8) +
(uint32_t)buf[1];
int active_power_sign = (buf[0] & (1 << 7)) ? -1 : 1;
int reactive_power_sign = (buf[0] & (1 << 6)) ? -1 : 1;
int sign = 1;
if (reg_type == REG_PARAM_SIGN_ACT) {
sign = active_power_sign;
} else if (reg_type == REG_PARAM_SIGN_REACT) {
sign = reactive_power_sign;
}
return (uint32_t)(((int32_t) magnitude * sign));
} else {
return ((uint32_t)buf[0] << 16) +
((uint32_t)buf[2] << 8) +
(uint32_t)buf[1];
}
} else {
if (reg_type == REG_PARAM_BE) {
return ((uint32_t)buf[0] << 8) +
((uint32_t)buf[1]);
} else {
return ((uint32_t)buf[1] << 8) +
((uint32_t)buf[0]);
}
}
}
uint64_t TMercury230Device::ReadRegister(PRegister reg)
{
switch (reg->Type) {
case REG_VALUE_ARRAY:
return ReadValueArray(reg->Address, 4).values[reg->Address & 0x03];
case REG_VALUE_ARRAY12:
return ReadValueArray(reg->Address, 3).values[reg->Address & 0x03];
case REG_PARAM:
case REG_PARAM_SIGN_ACT:
case REG_PARAM_SIGN_REACT:
case REG_PARAM_SIGN_IGNORE:
case REG_PARAM_BE:
return ReadParam( reg->Address & 0xffff, reg->ByteWidth(), (RegisterType) reg->Type);
default:
throw TSerialDeviceException("mercury230: invalid register type");
}
}
void TMercury230Device::EndPollCycle()
{
CachedValues.clear();
TSerialDevice::EndPollCycle();
}
// TBD: custom password?
// TBD: settings in uniel template: 9600 8N1, timeout ms = 1000