func getPendingMessages(bufferSize int) (result []SMS, err error) { log.Info("getPendingMessages ") allMessages, err := sdb.GetAll(bucket) if err != nil { log.Error(err) return } result = []SMS{} for _, _m := range allMessages { sms := SMS{} err := json.Unmarshal(_m, &sms) if err != nil { log.Error(err) return nil, err } if sms.Status != SMSProcessed && sms.Retries < SMSRetryLimit { result = append(result, sms) } if len(result) >= bufferSize { break } } return }
func main() { log.Info("main: ", "Initializing gosms") //load the config, abort if required config is not preset appConfig, err := gosms.GetConfig("conf.ini") if err != nil { log.Error("main: ", "Invalid config: ", err.Error(), " Aborting") os.Exit(1) } db, err := gosms.InitDB("sqlite3", "db.sqlite") if err != nil { log.Info("main: ", "Error initializing database: ", err, " Aborting") os.Exit(1) } defer db.Close() serverhost, _ := appConfig.Get("SETTINGS", "SERVERHOST") serverport, _ := appConfig.Get("SETTINGS", "SERVERPORT") _numDevices, _ := appConfig.Get("SETTINGS", "DEVICES") numDevices, _ := strconv.Atoi(_numDevices) log.Info("main: number of devices: ", numDevices) var modems []*modem.GSMModem for i := 0; i < numDevices; i++ { dev := fmt.Sprintf("DEVICE%v", i) _port, _ := appConfig.Get(dev, "COMPORT") _baud := 115200 //appConfig.Get(dev, "BAUDRATE") _devid, _ := appConfig.Get(dev, "DEVID") m := modem.New(_port, _baud, _devid) modems = append(modems, m) } _bufferSize, _ := appConfig.Get("SETTINGS", "BUFFERSIZE") bufferSize, _ := strconv.Atoi(_bufferSize) _bufferLow, _ := appConfig.Get("SETTINGS", "BUFFERLOW") bufferLow, _ := strconv.Atoi(_bufferLow) _loaderTimeout, _ := appConfig.Get("SETTINGS", "MSGTIMEOUT") loaderTimeout, _ := strconv.Atoi(_loaderTimeout) _loaderCountout, _ := appConfig.Get("SETTINGS", "MSGCOUNTOUT") loaderCountout, _ := strconv.Atoi(_loaderCountout) _loaderTimeoutLong, _ := appConfig.Get("SETTINGS", "MSGTIMEOUTLONG") loaderTimeoutLong, _ := strconv.Atoi(_loaderTimeoutLong) log.Info("main: Initializing worker") gosms.InitWorker(modems, bufferSize, bufferLow, loaderTimeout, loaderCountout, loaderTimeoutLong) log.Info("main: Initializing server") err = InitServer(serverhost, serverport) if err != nil { log.Error("main: ", "Error starting server: ", err.Error(), " Aborting") os.Exit(1) } }
func InitWorker(modems []*modem.GSMModem, bufferSize, bufferLow, loaderTimeout, countOut, loaderLongTimeout int) { log.Info("--- InitWorker") bufferMaxSize = bufferSize bufferLowCount = bufferLow messageLoaderTimeout = time.Duration(loaderTimeout) * time.Minute messageLoaderCountout = countOut messageLoaderLongTimeout = time.Duration(loaderLongTimeout) * time.Minute messages = make(chan SMS, bufferMaxSize) wakeupMessageLoader = make(chan bool, 1) wakeupMessageLoader <- true messageCountSinceLastWakeup = 0 timeOfLastWakeup = time.Now().Add((time.Duration(loaderTimeout) * -1) * time.Minute) //older time handles the cold start state of the system // its important to init messages channel before starting modems because nil // channel is non-blocking for i := 0; i < len(modems); i++ { modem := modems[i] err := modem.Connect() if err != nil { log.Error("InitWorker: error connecting", modem.DeviceId, err) continue } go processMessages(modem) } go messageLoader(bufferMaxSize, bufferLowCount) }
func (m *GSMModem) SendCommand(command string, waitForOk bool) string { log.Info("SendCommand: ", command) var status string = "" m.Port.Flush() _, err := m.Port.Write([]byte(command)) if err != nil { log.Error(err) return "" } buf := make([]byte, 32) var loop int = 1 if waitForOk { loop = 10 } for i := 0; i < loop; i++ { // ignoring error as EOF raises error on Linux n, _ := m.Port.Read(buf) if n > 0 { status = string(buf[:n]) log.Info("SendCommand: rcvd bytes: ", n, status) if strings.Contains(status, "OK\r\n") || strings.Contains(status, "ERROR\r\n") { break } } } return status }
func insertMessage(sms *SMS) error { log.Info("insertMessage ", sms) err := sdb.Save(bucket, sms.UUID, sms) if err != nil { log.Error("Error when inserting message: ", err) } return nil }
func GetMessages(filter string) (result []SMS, err error) { log.Info("GetMessages") allMessages, err := sdb.GetAll(bucket) if err != nil { log.Error(err) return } result = []SMS{} for _, _m := range allMessages { sms := SMS{} err := json.Unmarshal(_m, &sms) if err != nil { log.Error("Error when unmarshaling message: ", err) return nil, err } // if sms.Status != SMSProcessed && sms.Retries < SMSRetryLimit { result = append(result, sms) // } } return result, nil }
func GetStatusSummary() ([]int, error) { log.Info("GetStatusSummary") allMessages, err := GetMessages("") if err != nil { log.Error(err) return nil, err } statusSummary := make([]int, 3) for _, sms := range allMessages { statusSummary[sms.Status]++ } return statusSummary, nil }
func updateMessageStatus(sms SMS) error { log.Info("updateMessageStatus ", sms) encoded, err := sdb.Get(bucket, sms.UUID) if err != nil { log.Error("Error when getting message: ", err) return err } oldSms := SMS{} err = json.Unmarshal(encoded, &oldSms) if err != nil { log.Error("Error when unmarshaling message: ", err) return err } oldSms.Status = sms.Status oldSms.Retries = sms.Retries oldSms.Device = sms.Device oldSms.UpdatedAt = time.Now() err = sdb.Save(bucket, oldSms.UUID, oldSms) if err != nil { log.Error("Error when inserting message: ", err) } return err }
func messageLoader(bufferSize, minFill int) { // Load pending messages from database as needed for { /* - set a fairly long timeout for wakeup - if there are very few number of messages in the system and they failed at first go, and there are no events happening to call EnqueueMessage, those messages might get stalled in the system until someone knocks on the API door - we can afford a really long polling in this case */ timeout := make(chan bool, 1) go func() { time.Sleep(messageLoaderLongTimeout) timeout <- true }() log.Info("messageLoader: ", "waiting for wakeup call") select { case <-wakeupMessageLoader: log.Info("messageLoader: woken up by channel call") case <-timeout: log.Info("messageLoader: woken up by timeout") } if len(messages) >= bufferLowCount { //if we have sufficient number of messages to process, //don't bother hitting the database log.Info("messageLoader: ", "I have sufficient messages") continue } countToFetch := bufferMaxSize - len(messages) log.Info("messageLoader: ", "I need to fetch more messages", countToFetch) pendingMsgs, err := getPendingMessages(countToFetch) if err == nil { log.Info("messageLoader: ", len(pendingMsgs), " pending messages found") for _, msg := range pendingMsgs { messages <- msg } } else { log.Error(err) } } }
// dumps JSON data, used by log view. Methods allowed: GET func getLogsHandler(w http.ResponseWriter, r *http.Request) { log.Info("--- getLogsHandler") messages, _ := gosms.GetMessages("") summary, _ := gosms.GetStatusSummary() dayCount, _ := gosms.GetLast7DaysMessageCount() logs := SMSDataResponse{ Status: 200, Message: "ok", Summary: summary, DayCount: dayCount, Messages: messages, } var toWrite []byte toWrite, err := json.Marshal(logs) if err != nil { log.Error(err) //lets just depend on the server to raise 500 } w.Header().Set("Content-type", "application/json") w.Write(toWrite) }
// push sms, allowed methods: POST func sendSMSHandler(w http.ResponseWriter, r *http.Request) { log.Info("--- sendSMSHandler") w.Header().Set("Content-type", "application/json") //TODO: validation r.ParseForm() mobile := r.FormValue("mobile") message := r.FormValue("message") uuid := uuid.NewV4() sms := &gosms.SMS{UUID: uuid.String(), Mobile: mobile, Body: message, Retries: 0, CreatedAt: time.Now()} gosms.EnqueueMessage(sms, true) smsresp := SMSResponse{Status: 200, Message: "ok"} var toWrite []byte toWrite, err := json.Marshal(smsresp) if err != nil { log.Error(err) //lets just depend on the server to raise 500 } w.Write(toWrite) }
func GetLast7DaysMessageCount() (map[string]int, error) { log.Info("GetLast7DaysMessageCount") allMessages, err := GetMessages("") if err != nil { log.Error(err) return nil, err } dayCount := make(map[string]int) fromDate := time.Now().Add(-time.Hour * 24 * 8) for _, sms := range allMessages { if sms.CreatedAt.After(fromDate) { createdAt := sms.CreatedAt.Format("2006-01-02") count, ok := dayCount[createdAt] if ok { count++ } else { count = 1 } dayCount[createdAt] = count } } return dayCount, nil }