Support PM67 voltage fields in UMP logger v3
This commit is contained in:
55
c/set-protocol/include/ump_protocol.h
Normal file
55
c/set-protocol/include/ump_protocol.h
Normal file
@@ -0,0 +1,55 @@
|
||||
/* UMP logger v2 (PM35), independent of PM67 and SET protocol v2.
|
||||
* Octets use 16-bit storage: C28x has CHAR_BIT=16 and no uint8_t.
|
||||
* All lengths below count wire octets or register words, never sizeof bytes.
|
||||
*/
|
||||
#ifndef UMP_PROTOCOL_H
|
||||
#define UMP_PROTOCOL_H
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
#define UMP_BASE 0x1000U
|
||||
#define UMP_DATA 0x1010U
|
||||
#define UMP_LIVE 0x1100U
|
||||
#define UMP_MARKER 0x1F00U
|
||||
#define UMP_MAX_WORDS 124U
|
||||
#define UMP_RTU_CAPACITY 253U
|
||||
typedef uint16_t ump_octet;
|
||||
/* Called under caller's snapshot lock. write returns 1 on success, 0 otherwise. */
|
||||
typedef struct {
|
||||
void *context;
|
||||
uint16_t (*read)(void *context, uint16_t address);
|
||||
int (*write)(void *context, uint16_t address, uint16_t value);
|
||||
} ump_backend;
|
||||
/* Returns Modbus exception 0/1/2/3. CAN has stricter whole-block reads. */
|
||||
unsigned ump_validate(unsigned function, uint16_t address, uint16_t value, int can);
|
||||
/* reply capacity is >=124 words. Errors and writes return address/value echo. */
|
||||
unsigned ump_prepare(const ump_backend *backend, unsigned function,
|
||||
uint16_t address, uint16_t value, int can, uint16_t *reply, unsigned *error);
|
||||
uint16_t ump_crc(const ump_octet *data, size_t count);
|
||||
/* Mode 1..16; kind 0=request, 1=normal telemetry, 2=logger reply; invalid ->0. */
|
||||
uint32_t ump_can_id(unsigned mode, unsigned kind);
|
||||
/* Returns 0 for non-service frames, malformed size, or zero token. */
|
||||
unsigned ump_can_request(const ump_backend *backend, const ump_octet *request,
|
||||
size_t size, uint16_t *reply, unsigned *token, unsigned *error);
|
||||
/* count must be an even number 2..124; index counts pairs. Returns 8 or 0. */
|
||||
unsigned ump_can_fragment(const uint16_t *words, unsigned count, unsigned index,
|
||||
unsigned token, unsigned error, ump_octet *reply);
|
||||
/* Complete RTU frame only, after UART framing. Ignores bad CRC/foreign address.
|
||||
* Caller provides >=253 octets; no writes occur on insufficient capacity.
|
||||
* Device address 1..247; no broadcasts or legacy padding in this extension.
|
||||
*/
|
||||
size_t ump_rtu(const ump_backend *backend, unsigned device,
|
||||
const ump_octet *request, size_t size, ump_octet *reply, size_t capacity);
|
||||
/* Response for an already CRC-checked/framed request (legacy UART receiver). */
|
||||
size_t ump_rtu_response(const ump_backend *backend, unsigned device, unsigned function,
|
||||
uint16_t address, uint16_t value, ump_octet *reply, size_t capacity);
|
||||
/* Serialize a captured reply after releasing the snapshot lock. Returns 0 on
|
||||
* invalid count/capacity; error replies need no words. */
|
||||
size_t ump_rtu_encode(unsigned device, unsigned function, unsigned error,
|
||||
const uint16_t *words, unsigned count, ump_octet *reply, size_t capacity);
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
34
c/set-protocol/ports/tms320f28335/ump_tms28335.c
Normal file
34
c/set-protocol/ports/tms320f28335/ump_tms28335.c
Normal file
@@ -0,0 +1,34 @@
|
||||
#include "ump_tms28335.h"
|
||||
extern unsigned int ump_log_read(unsigned int address);
|
||||
extern int ump_log_write(unsigned int address, unsigned int value);
|
||||
static uint16_t read_word(void *context, uint16_t address)
|
||||
{ (void)context; return (uint16_t)ump_log_read(address); }
|
||||
static int write_word(void *context, uint16_t address, uint16_t value)
|
||||
{ (void)context; return ump_log_write(address,value); }
|
||||
unsigned ump_tms28335_prepare(unsigned fn, uint16_t a, uint16_t n,
|
||||
uint16_t *reply, unsigned *error)
|
||||
{
|
||||
const ump_backend backend = {0,read_word,write_word};
|
||||
return ump_prepare(&backend,fn,a,n,1,reply,error);
|
||||
}
|
||||
size_t ump_tms28335_rtu(unsigned device, unsigned fn, uint16_t a,
|
||||
uint16_t n, ump_octet *reply, size_t capacity)
|
||||
{
|
||||
const ump_backend backend = {0,read_word,write_word};
|
||||
return ump_rtu_response(&backend,device,fn,a,n,reply,capacity);
|
||||
}
|
||||
unsigned ump_tms28335_capture(unsigned fn, uint16_t a, uint16_t n,
|
||||
uint16_t *words, unsigned *error)
|
||||
{
|
||||
const ump_backend backend = {0,read_word,write_word};
|
||||
return ump_prepare(&backend,fn,a,n,0,words,error);
|
||||
}
|
||||
unsigned ump_tms28335_fragment(const uint16_t *words, unsigned count, unsigned index,
|
||||
unsigned token, unsigned error, uint32_t *mdl, uint32_t *mdh)
|
||||
{
|
||||
ump_octet frame[8];
|
||||
if (!mdl || !mdh || !ump_can_fragment(words,count,index,token,error,frame)) return 0;
|
||||
*mdl=((uint32_t)frame[0]<<24)|((uint32_t)frame[1]<<16)|(frame[2]<<8)|frame[3];
|
||||
*mdh=((uint32_t)frame[4]<<24)|((uint32_t)frame[5]<<16)|(frame[6]<<8)|frame[7];
|
||||
return 8;
|
||||
}
|
||||
15
c/set-protocol/ports/tms320f28335/ump_tms28335.h
Normal file
15
c/set-protocol/ports/tms320f28335/ump_tms28335.h
Normal file
@@ -0,0 +1,15 @@
|
||||
#ifndef UMP_TMS28335_H
|
||||
#define UMP_TMS28335_H
|
||||
#include "../../include/ump_protocol.h"
|
||||
/* Application provides ump_log_read/write; caller holds the snapshot IRQ lock.
|
||||
* No peripheral registers, ISR installation or transport waits in this port. */
|
||||
unsigned ump_tms28335_prepare(unsigned function, uint16_t address, uint16_t value,
|
||||
uint16_t *reply, unsigned *error);
|
||||
size_t ump_tms28335_rtu(unsigned device, unsigned function, uint16_t address,
|
||||
uint16_t value, ump_octet *reply, size_t capacity);
|
||||
unsigned ump_tms28335_capture(unsigned function, uint16_t address, uint16_t value,
|
||||
uint16_t *words, unsigned *error);
|
||||
/* Convert one pair to TI eCAN MDL/MDH register values; returns 8 or 0. */
|
||||
unsigned ump_tms28335_fragment(const uint16_t *words, unsigned count, unsigned index,
|
||||
unsigned token, unsigned error, uint32_t *mdl, uint32_t *mdh);
|
||||
#endif
|
||||
105
c/set-protocol/src/ump_protocol.c
Normal file
105
c/set-protocol/src/ump_protocol.c
Normal file
@@ -0,0 +1,105 @@
|
||||
#include "../include/ump_protocol.h"
|
||||
static uint16_t get16(const ump_octet *p)
|
||||
{ return (uint16_t)(((p[0]&255U)<<8)|(p[1]&255U)); }
|
||||
static void put16(ump_octet *p, uint16_t v)
|
||||
{ p[0] = v>>8; p[1] = v&255U; }
|
||||
unsigned ump_validate(unsigned fn, uint16_t a, uint16_t n, int can)
|
||||
{
|
||||
if (fn == 6) return a >= UMP_BASE && a <= UMP_BASE+3 ? 0 : 2;
|
||||
if (fn != 3) return 1;
|
||||
if (can) return ((a == UMP_BASE && n == 16) ||
|
||||
(a == UMP_DATA && (n == 40 || n == 80 || n == 120 || n == 42 || n == 84)) ||
|
||||
(a == UMP_LIVE && n == 124)) ? 0 : 2;
|
||||
if (!n || n > 125) return 2;
|
||||
return ((a >= UMP_BASE && a < UMP_BASE+16 && n <= UMP_BASE+16-a) ||
|
||||
(a >= UMP_DATA && a < UMP_DATA+120 && n <= UMP_DATA+120-a) ||
|
||||
(a == UMP_LIVE && n == 124)) ? 0 : 2;
|
||||
}
|
||||
unsigned ump_prepare(const ump_backend *b, unsigned fn, uint16_t a,
|
||||
uint16_t n, int can, uint16_t *out, unsigned *error)
|
||||
{
|
||||
unsigned i;
|
||||
if (!out || !error) return 0;
|
||||
*error = ump_validate(fn,a,n,can);
|
||||
if (!*error) {
|
||||
if (!b || (fn == 3 ? !b->read : !b->write)) *error = 3;
|
||||
else if (fn == 3) {
|
||||
for (i=0;i<n;i++) out[i] = b->read(b->context,(uint16_t)(a+i));
|
||||
return n;
|
||||
} else if (!b->write(b->context,a,n)) *error = 3;
|
||||
}
|
||||
out[0]=a; out[1]=n;
|
||||
return 2;
|
||||
}
|
||||
uint16_t ump_crc(const ump_octet *p, size_t n)
|
||||
{
|
||||
uint16_t crc=0xFFFFU;
|
||||
unsigned i;
|
||||
while (n--) {
|
||||
crc ^= *p++ & 255U;
|
||||
for (i=0;i<8;i++) crc=(uint16_t)((crc>>1)^((crc&1U)?0xA001U:0));
|
||||
}
|
||||
return crc;
|
||||
}
|
||||
uint32_t ump_can_id(unsigned mode, unsigned kind)
|
||||
{ return mode>=1 && mode<=16 && kind<=2 ? 0xBA0000UL+kind*16UL+mode-1 : 0; }
|
||||
unsigned ump_can_request(const ump_backend *b, const ump_octet *r, size_t size,
|
||||
uint16_t *out, unsigned *token, unsigned *error)
|
||||
{
|
||||
size_t i;
|
||||
if (!r || !out || !token || !error || size!=8) return 0;
|
||||
for (i=0;i<size;i++) if (r[i]>255) return 0;
|
||||
if (get16(r+4)!=UMP_MARKER || !r[6]) return 0;
|
||||
*token=r[6];
|
||||
return ump_prepare(b,r[7],get16(r),get16(r+2),1,out,error);
|
||||
}
|
||||
unsigned ump_can_fragment(const uint16_t *words, unsigned count, unsigned index,
|
||||
unsigned token, unsigned error, ump_octet *out)
|
||||
{
|
||||
if (!words || !out || count<2 || count>124 || (count&1) ||
|
||||
index>=count/2 || !token || token>255 || error>255) return 0;
|
||||
put16(out,words[index*2]); put16(out+2,words[index*2+1]);
|
||||
put16(out+4,(uint16_t)(UMP_MARKER+index)); out[6]=(ump_octet)token; out[7]=(ump_octet)error;
|
||||
return 8;
|
||||
}
|
||||
size_t ump_rtu(const ump_backend *b, unsigned device, const ump_octet *r,
|
||||
size_t size, ump_octet *out, size_t capacity)
|
||||
{
|
||||
unsigned i;
|
||||
if (!r || !out || size!=8 || capacity<UMP_RTU_CAPACITY ||
|
||||
!device || device>247 || r[0]!=device) return 0;
|
||||
for (i=0;i<8;i++) if (r[i]>255) return 0;
|
||||
if (ump_crc(r,8)) return 0;
|
||||
return ump_rtu_response(b,device,r[1],get16(r+2),get16(r+4),out,capacity);
|
||||
}
|
||||
size_t ump_rtu_response(const ump_backend *b, unsigned device, unsigned fn,
|
||||
uint16_t address, uint16_t value, ump_octet *out, size_t capacity)
|
||||
{
|
||||
uint16_t words[UMP_MAX_WORDS];
|
||||
unsigned count,error;
|
||||
if (!out || capacity<UMP_RTU_CAPACITY || !device || device>247 || fn>127) return 0;
|
||||
count=ump_prepare(b,fn,address,value,0,words,&error);
|
||||
return ump_rtu_encode(device,fn,error,words,count,out,capacity);
|
||||
}
|
||||
size_t ump_rtu_encode(unsigned device, unsigned fn, unsigned error,
|
||||
const uint16_t *words, unsigned count, ump_octet *out, size_t capacity)
|
||||
{
|
||||
unsigned i;
|
||||
size_t n;
|
||||
uint16_t crc;
|
||||
if (!out || !device || device>247 || fn>127 || error>255) return 0;
|
||||
if (!error && (!words || (fn!=3 && fn!=6) ||
|
||||
(fn==3 && (!count || count>124)) || (fn==6 && count!=2))) return 0;
|
||||
n=error ? 5 : (fn==6 ? 8 : 5+count*2);
|
||||
if (capacity<n) return 0;
|
||||
out[0]=(ump_octet)device; out[1]=(ump_octet)fn;
|
||||
if (error) { out[1]|=128; out[2]=(ump_octet)error; n=3; }
|
||||
else if (fn==6) { put16(out+2,words[0]); put16(out+4,words[1]); n=6; }
|
||||
else {
|
||||
out[2]=(ump_octet)(count*2);
|
||||
for (i=0;i<count;i++) put16(out+3+i*2,words[i]);
|
||||
n=3+count*2;
|
||||
}
|
||||
crc=ump_crc(out,n); out[n++]=crc&255U; out[n++]=crc>>8;
|
||||
return n;
|
||||
}
|
||||
@@ -63,6 +63,8 @@ FIELDS = (
|
||||
('adc1', 'АЦП канал 1', 37, None),
|
||||
('adc2', 'АЦП канал 2', 38, None),
|
||||
('adc3', 'АЦП канал 3', 39, None),
|
||||
('zpt1', 'ЗПТ1: U1 + U2 (ПМ67, рег. 306)', 40, None),
|
||||
('zpt2', 'ЗПТ2: U3 + U4 (ПМ67, рег. 9)', 41, None),
|
||||
)
|
||||
|
||||
|
||||
@@ -71,7 +73,7 @@ def request(controller: int, function: int, address: int, value: int) -> bytes:
|
||||
raise ValueError('Недопустимый адрес контроллера или значение')
|
||||
if function == 3:
|
||||
valid = ((address == BASE and value == 16) or
|
||||
(address == DATA and 1 <= value <= 120 and value % WORDS == 0) or (address == LIVE and value == 124) or (address == EMU_CONTROL and value == 4)
|
||||
(address == DATA and 1 <= value <= 120 and (value % WORDS == 0 or value in (42, 84))) or (address == LIVE and value == 124) or (address == EMU_CONTROL and value == 4)
|
||||
or (address == FAST_CAPABILITY and value == 4)
|
||||
or (FAST_DATA <= address < FAST_DATA + CAPACITY * WORDS
|
||||
and (address - FAST_DATA) % WORDS == 0
|
||||
@@ -96,25 +98,27 @@ class Status:
|
||||
archive_count: int
|
||||
post_ms: int
|
||||
live_state: int
|
||||
words: int = 40
|
||||
capacity: int = 800
|
||||
|
||||
@classmethod
|
||||
def decode(cls, words):
|
||||
if (len(words) != 16 or words[0:2] != (0x554D, 2) or
|
||||
words[4] != WORDS or words[5] != CAPACITY):
|
||||
raise ValueError('Прошивка не поддерживает логгер УМП v2')
|
||||
if (len(words) != 16 or words[0] != 0x554D or
|
||||
(words[1], words[4], words[5]) not in ((2, 40, 800), (3, 42, 760))):
|
||||
raise ValueError('Прошивка не поддерживает логгер УМП v2/v3')
|
||||
if words[9] != 6:
|
||||
raise ValueError('Логгер УМП доступен только на плате нагрузки dsk_LOAD')
|
||||
if words[2] not in (0, 1, 2, 3) or words[3] > CAPACITY or not 1 <= words[6] <= 1000 or words[11] != 1000 or words[13] > CAPACITY or words[14] > 1000 or words[15] not in (0, 1, 3):
|
||||
if words[2] not in (0, 1, 2, 3) or words[3] > words[5] or not 1 <= words[6] <= 1000 or words[11] != 1000 or words[13] > words[5] or words[14] > 1000 or words[15] not in (0, 1, 3):
|
||||
raise ValueError('Повреждён статус логгера')
|
||||
return cls(words[2], words[3], words[6], words[10], words[11], *words[12:16])
|
||||
return cls(words[2], words[3], words[6], words[10], words[11], *words[12:16], words[4], words[5])
|
||||
|
||||
|
||||
def sample(words, frequency=1000):
|
||||
if len(words) != WORDS or frequency <= 0:
|
||||
if len(words) not in (40, 42) or frequency <= 0:
|
||||
raise ValueError('Неверная запись логгера')
|
||||
result = {'time_ms': (words[0] | words[1] << 16) * 1000 / frequency, 'event': words[2]}
|
||||
for key, _label, index, bit in FIELDS:
|
||||
value = words[index]
|
||||
value = words[index] if index < len(words) else float('nan')
|
||||
if bit is not None:
|
||||
value = (value >> bit) & 1
|
||||
elif 10 <= index <= 13 and value >= 32768:
|
||||
|
||||
@@ -23,7 +23,7 @@ def build_request(mode, token, function, address, value):
|
||||
if not 1 <= token <= 255:
|
||||
raise ValueError('Неверный номер CAN-запроса')
|
||||
valid = ((function == 3 and ((address == BASE and value == 16) or
|
||||
(address == DATA and value in (40, 80, 120)) or
|
||||
(address == DATA and value in (40, 80, 120, 42, 84)) or
|
||||
(address == LIVE and value == MAX_WORDS) or (address == EMU_CONTROL and value == 4))) or
|
||||
(function == 6 and ((BASE <= address <= BASE+3 and 0 <= value <= 65535) or
|
||||
(address == EMU_CONTROL+2 and value in (0, 1)))))
|
||||
|
||||
Reference in New Issue
Block a user