feat(gas-logger): добавь непрерывный логгер и JSON-карту

This commit is contained in:
2026-10-01 22:54:29 +03:00
parent 675ce063f0
commit f7a6f767e1
22 changed files with 1250 additions and 0 deletions
+161
View File
@@ -0,0 +1,161 @@
#include "gas_logger.h"
#include <stddef.h>
#include <string.h>
static uint16_t width(const gas_logger *l) { return l->config.channels + GL_HEADER_WORDS; }
static int service(const gl_config *c, uint16_t a)
{
uint32_t end = (uint32_t)c->base + GL_DATA_OFFSET +
(uint32_t)c->capacity * (c->channels + GL_HEADER_WORDS);
return a >= c->base && (uint32_t)a < end;
}
gl_result gl_init(gas_logger *l, const gl_config *c, const gl_port *p,
uint16_t *b0, uint16_t *b1, uint32_t words)
{
uint16_t i,j;
uint32_t needed;
if (!l || !c || !p || !b0 || !b1 || b0 == b1 || !c->map ||
!p->enter || !p->leave || !p->source || !c->capacity ||
!c->channels || c->channels > GL_MAX_CHANNELS) return GL_ARGUMENT;
needed = (uint32_t)c->capacity * (c->channels + GL_HEADER_WORDS);
if (needed > words || (uint32_t)c->base + GL_DATA_OFFSET + needed > 65536UL)
return GL_ARGUMENT;
for (i=0;i<c->channels;i++) {
if (service(c,c->map[i].gas) || service(c,c->map[i].modbus) ||
service(c,c->map[i].can)) return GL_ARGUMENT;
for (j=0;j<i;j++)
if (c->map[i].gas == c->map[j].gas ||
c->map[i].modbus == c->map[j].modbus ||
c->map[i].can == c->map[j].can) return GL_ARGUMENT;
}
memset(l,0,sizeof(*l)); l->config=*c; l->port=*p; l->banks[0]=b0; l->banks[1]=b1;
return GL_OK;
}
gl_result gl_capture(gas_logger *l, uint32_t time, uint16_t event)
{
uint16_t values[GL_MAX_CHANNELS],i,*record;
uint32_t irq = l->port.enter(l->port.user);
if (!l->recording) { l->port.leave(l->port.user,irq); return GL_BUSY; }
for (i=0;i<l->config.channels;i++) {
if (!l->port.source(l->port.user,l->config.map[i].gas,&values[i])) {
l->source_errors++; l->port.leave(l->port.user,irq); return GL_SOURCE_ERROR;
}
}
record=l->banks[l->active]+(uint32_t)l->next*width(l);
record[0]=(uint16_t)time; record[1]=(uint16_t)(time>>16);
record[2]=(uint16_t)l->sequence; record[3]=(uint16_t)(l->sequence>>16);
record[4]=event;
for (i=0;i<l->config.channels;i++) record[GL_HEADER_WORDS+i]=values[i];
l->sequence++;
if (++l->next == l->config.capacity) l->next=0;
if (l->count < l->config.capacity) l->count++;
l->port.leave(l->port.user,irq); return GL_OK;
}
static gl_result snapshot(gas_logger *l)
{
if (l->pinned) { l->missed++; return GL_BUSY; }
if (!l->count) return GL_EMPTY;
l->snap_count=l->count; l->snap_next=l->next;
l->active^=1U; l->count=0; l->next=0; l->pinned=1;
l->generation++; if (!l->generation) l->generation=1;
return GL_OK;
}
gl_result gl_snapshot(gas_logger *l)
{
uint32_t irq=l->port.enter(l->port.user);
gl_result r=snapshot(l); l->port.leave(l->port.user,irq); return r;
}
static uint16_t status(const gas_logger *l, uint16_t offset)
{
switch(offset) {
case 0:return GL_MAGIC; case 1:return GL_VERSION;
case 2:return l->recording | (l->pinned<<1);
case 3:return l->config.channels; case 4:return width(l);
case 5:return l->config.capacity; case 6:return l->count; case 7:return l->snap_count;
case 8:return (uint16_t)l->generation; case 9:return (uint16_t)(l->generation>>16);
case 10:return (uint16_t)l->config.schema_id; case 11:return (uint16_t)(l->config.schema_id>>16);
case 12:return (uint16_t)l->sequence; case 13:return (uint16_t)(l->sequence>>16);
case 14:return (uint16_t)l->missed; case 15:return (uint16_t)(l->missed>>16);
case 16:return (uint16_t)l->source_errors; case 17:return (uint16_t)(l->source_errors>>16);
default:return 0;
}
}
static gl_result read_locked(gas_logger *l, uint16_t a, uint16_t *out, uint16_t n)
{
uint16_t i,j,slot;
uint32_t off;
if (a>=l->config.base && (uint32_t)a+n <= (uint32_t)l->config.base+18) {
for(i=0;i<n;i++) out[i]=status(l,(uint16_t)(a+i-l->config.base));
return GL_OK;
}
if ((uint32_t)a >= (uint32_t)l->config.base+GL_DATA_OFFSET) {
off=(uint32_t)a-l->config.base-GL_DATA_OFFSET;
if (l->pinned && off+n <= (uint32_t)l->snap_count*width(l)) {
for(i=0;i<n;i++) {
slot=(uint16_t)((off+i)/width(l));
if(l->snap_count==l->config.capacity)
slot=(uint16_t)(((uint32_t)slot+l->snap_next)%l->config.capacity);
out[i]=l->banks[l->active^1U][(uint32_t)slot*width(l)+(off+i)%width(l)];
}
return GL_OK;
}
}
/* A live signal block must be completely mapped before any read. */
for(i=0;i<n;i++) {
for(j=0;j<l->config.channels;j++) if(l->config.map[j].gas==a+i) break;
if(j==l->config.channels) return GL_ADDRESS;
}
for(i=0;i<n;i++)
if(!l->port.source(l->port.user,(uint16_t)(a+i),&out[i])) return GL_SOURCE_ERROR;
return GL_OK;
}
gl_result gl_read(gas_logger *l, uint16_t a, uint16_t *out, uint16_t n)
{
uint16_t temp[125],i;
uint32_t irq;
gl_result r;
if(!out || !n || n>125 || (uint32_t)a+n>65536UL) return GL_ARGUMENT;
irq=l->port.enter(l->port.user); r=read_locked(l,a,temp,n);
if(r==GL_OK) for(i=0;i<n;i++) out[i]=temp[i];
l->port.leave(l->port.user,irq); return r;
}
gl_result gl_write(gas_logger *l, uint16_t a, uint16_t v)
{
uint32_t irq=l->port.enter(l->port.user);
gl_result r=GL_OK;
if((uint32_t)a==(uint32_t)l->config.base+GL_RELEASE_LO)
l->release_generation=(l->release_generation&0xFFFF0000UL)|v;
else if((uint32_t)a==(uint32_t)l->config.base+GL_RELEASE_HI)
l->release_generation=(l->release_generation&0xFFFFUL)|((uint32_t)v<<16);
else if((uint32_t)a!=(uint32_t)l->config.base+GL_COMMAND) r=GL_ADDRESS;
else if(v==GL_START) l->recording=1;
else if(v==GL_SNAPSHOT) r=snapshot(l);
else if(v==GL_RELEASE) {
if(!l->pinned || l->release_generation!=l->generation) r=GL_STALE;
else { l->pinned=0; l->snap_count=0; }
} else r=GL_ARGUMENT;
l->port.leave(l->port.user,irq); return r;
}
static gl_result alias_read(gas_logger *l,uint16_t a,uint16_t *out,uint16_t n,int can)
{
uint16_t i,j,gas=0,found;
if(!n || n>125 || (uint32_t)a+n>65536UL) return GL_ARGUMENT;
if(service(&l->config,a)) return gl_read(l,a,out,n);
for(i=0;i<n;i++) {
found=0;
for(j=0;j<l->config.channels;j++)
if((can?l->config.map[j].can:l->config.map[j].modbus)==a+i) {
if(!i) gas=l->config.map[j].gas;
if((uint32_t)gas+i!=l->config.map[j].gas) return GL_ADDRESS;
found=1;break;
}
if(!found) return GL_ADDRESS;
}
return gl_read(l,gas,out,n);
}
gl_result gl_modbus_read(gas_logger *l,uint16_t a,uint16_t *o,uint16_t n)
{ return alias_read(l,a,o,n,0); }
gl_result gl_can_read(gas_logger *l,uint16_t a,uint16_t *o,uint16_t n)
{ return alias_read(l,a,o,n,1); }
gl_result gl_modbus_write(gas_logger *l,uint16_t a,uint16_t v)
{ return gl_write(l,a,v); }
+13
View File
@@ -0,0 +1,13 @@
#include "gas_logger_pcan.h"
static pcan_gas_status_t result(gl_result r)
{ return r==GL_OK ? PCAN_GAS_OK : r==GL_ADDRESS ? PCAN_GAS_NO_REG : PCAN_GAS_REJECTED; }
static pcan_gas_status_t read_word(const pcan_gas_region_t *r,uint16_t o,uint16_t *v)
{ return result(gl_can_read(r->user,(uint16_t)(r->base+o),v,1)); }
static pcan_gas_status_t write_word(const pcan_gas_region_t *r,uint16_t o,uint16_t v)
{ return result(gl_write(r->user,(uint16_t)(r->base+o),v)); }
void gl_pcan_region_init(pcan_gas_region_t *r,gas_logger *l)
{
r->base=l->config.base;
r->count=(uint16_t)(GL_DATA_OFFSET+(uint32_t)l->config.capacity*(l->config.channels+GL_HEADER_WORDS));
r->storage=0;r->read=read_word;r->write=write_word;r->flags=0;r->user=l;r->name="gas-logger";
}