From: jweigele Date: Fri, 16 Dec 2022 07:15:31 +0000 (-0800) Subject: Attempt to deal with serial losing sync in DIY handling X-Git-Url: http://git.hexthepla.net/?a=commitdiff_plain;h=84c02953a03eb77231feaea142dddbb7e5576567;p=rabbit_go Attempt to deal with serial losing sync in DIY handling * Every so often the zigbee messages from a pico will drop a letter * My GUESS is this is due to the zigbee hardware not resyncing on each new byte (inherent to the protocol, but prolly not in this homegrown firmware) Anyway, have the sending pico calc a crc32 and put it on the end of the json. If anything up to the action map doesn't decode properly, well, something's obviously wrong and we can just ignore that transmission. If it all decodes properly but something's still off, we can index into the crc32 value and compare (and then reject). Since we debug messages don't use action at all, they're already ignored, and on send we'll just try again later if anything was garbled. --- diff --git a/helper/helper.go b/helper/helper.go index 24527b4..3fb8468 100644 --- a/helper/helper.go +++ b/helper/helper.go @@ -7,6 +7,9 @@ import ( "crypto/x509" "encoding/json" "fmt" + "hash/crc32" + "os" + "regexp" "io/ioutil" "strings" @@ -174,6 +177,47 @@ func StartConsuming(rabbit RabbitConfig) (<-chan amqp.Delivery, error) { return deliveries, err } +func validateCRC32(checkMap map[string]interface{}, data []byte, enforce bool) bool { + // constant polynomial that's used by micropython for crc32 calc + crc32table := crc32.MakeTable(0xEDB88320) + _, crcPresent := checkMap["crc32"] + if !crcPresent { + logger.V(3).Info("No CRC32 present in action body", "action", checkMap, "enforce", enforce) + // returns invalid if enforce is set, otherwise true - ignores the crc + return !enforce + } else { + // crc32 is present, now we have to check the map appropriately + // first, strip off the crc itself + targetHash := checkMap["crc32"].(string) + regexp, err := regexp.Compile("(, )?\"crc32\"[^\"]+\"[^\"]+\"") + if err != nil { + logger.Error(nil, "Not able to compile regex matcher") + os.Exit(1) + } + match := regexp.ReplaceAllString(string(data), "") + + //delete(checkMap, "crc32") + // remarshal, now without the crc key + /*targetJson, err := json.MarshalIndent(checkMap, "", " ") + if err != nil { + logger.Error(err, "we got an error remarshalling action map data, just like exit", "action", checkMap) + os.Exit(1) + }*/ + targetJson := []byte(match) + // calculate the hash itself, and output as a lowercase hex string + checkHash := fmt.Sprintf("%x", crc32.Checksum(targetJson, crc32table)) + if targetHash == checkHash { + // hashes match! + logger.V(3).Info("Hashes match", "targetHash", targetHash, "checkHash", checkHash, "action", checkMap) + return true + } else { + logger.Error(nil, "CRC32 hashes do not match", "targetHash", targetHash, "checkHash", checkHash, "action", targetJson, "data", match) + return false + } + } + +} + // DecodeDelivery - takes a presumably byte-formatted json stream and makes it a map for us // will return errors if things don't actually work out that well func DecodeDelivery(delivery amqp.Delivery) (map[string]interface{}, error) { @@ -194,7 +238,12 @@ func DecodeDelivery(delivery amqp.Delivery) (map[string]interface{}, error) { var decode map[string]interface{} err := json.Unmarshal([]byte(result["action"].(string)), &decode) if err == nil { - result["action"] = decode + // if we do have a CRC32 present, check its validity, otherwise enforce and return error + if validateCRC32(decode, []byte(result["action"].(string)), true) { + result["action"] = decode + } else { + return result, err + } } // otherwise, just leave it alone and return as-is } diff --git a/lights/main.go b/lights/main.go index 953f5d0..115e685 100644 --- a/lights/main.go +++ b/lights/main.go @@ -574,7 +574,7 @@ func (manager *RGBManager) init(rabbit *helper.RabbitConfig) { for _, bindTopic := range bindTopics { err := helper.Bind(bindTopic, manager.rabbitHelper) if err != nil { - logger.Error(err, "unable to bind successfully to exchange", "routingKey", "leds") + logger.Error(err, "unable to bind successfully to exchange", "routingKey", bindTopic) os.Exit(1) } }