61 lines
1.5 KiB
Go
61 lines
1.5 KiB
Go
package aa55
|
|
|
|
import (
|
|
"sync"
|
|
"time"
|
|
)
|
|
|
|
const cacheTTL = 2 * time.Second
|
|
|
|
// cache is the package-level response cache shared across all AA55UDP plugin
|
|
// instances. Sharing at package level ensures that multiple source blocks for
|
|
// the same (host, pdu) pair — e.g. the four Ppv string registers all using
|
|
// READ 125 @ 0x891C — share one UDP exchange per poll cycle.
|
|
//
|
|
// TTL is 2 s: long enough to serve all source blocks within one evcc poll
|
|
// cycle (which completes in well under 1 s), short enough that the next cycle
|
|
// always fetches fresh data.
|
|
var cache = newResponseCache()
|
|
|
|
type cacheEntry struct {
|
|
payload []byte
|
|
expiresAt time.Time
|
|
}
|
|
|
|
type responseCache struct {
|
|
mu sync.Mutex
|
|
data map[string]cacheEntry
|
|
}
|
|
|
|
func newResponseCache() *responseCache {
|
|
return &responseCache{data: make(map[string]cacheEntry)}
|
|
}
|
|
|
|
// get returns the cached payload if it exists and is fresh, or (nil, false)
|
|
// otherwise. Expired entries are deleted on access. The map lookup
|
|
// m[string(key)] is alloc-free — the Go compiler elides the conversion.
|
|
func (c *responseCache) get(key []byte) ([]byte, bool) {
|
|
c.mu.Lock()
|
|
defer c.mu.Unlock()
|
|
|
|
entry, ok := c.data[string(key)]
|
|
if !ok {
|
|
return nil, false
|
|
}
|
|
if time.Now().After(entry.expiresAt) {
|
|
delete(c.data, string(key))
|
|
return nil, false
|
|
}
|
|
return entry.payload, true
|
|
}
|
|
|
|
// put inserts or overwrites a payload in the cache.
|
|
func (c *responseCache) put(key, payload []byte) {
|
|
c.mu.Lock()
|
|
defer c.mu.Unlock()
|
|
|
|
c.data[string(key)] = cacheEntry{
|
|
payload: payload,
|
|
expiresAt: time.Now().Add(cacheTTL),
|
|
}
|
|
}
|