Commit 2f9694ff by 이병복

v2 add

parent 4d1c8095
package main
import (
"fmt"
"io"
"net"
"time"
)
func runClient() error {
var conn net.Conn
var err error
for i := 0; i < config.MaxRetries; i++ {
conn, err = net.Dial("tcp", config.ServerAddress)
if err == nil {
break
}
fmt.Printf("Connection attempt %d failed: %v\n", i+1, err)
time.Sleep(time.Duration(config.ReconnectInterval) * time.Second)
}
if err != nil {
return fmt.Errorf("failed to connect after %d attempts", config.MaxRetries)
}
defer conn.Close()
fmt.Println("Connected to server")
for {
if err := handleConnection(conn); err != nil {
resetReciverConnectGPIO()
return err
}
}
}
func handleConnection(conn net.Conn) error {
setReciverConnectGPIO()
for {
conn.SetReadDeadline(time.Now().Add(time.Duration(config.ReadTimeout) * time.Second))
sizeBuf := make([]byte, 1)
_, err := conn.Read(sizeBuf)
if err != nil {
return fmt.Errorf("error reading size: %v", err)
}
size := int(sizeBuf[0])
if size == 0 {
continue
}
fmt.Printf("Received size: %d\n", size)
buf := make([]byte, size-1)
_, err = io.ReadFull(conn, buf)
if err != nil {
return fmt.Errorf("error reading data: %v", err)
}
fmt.Printf("Received data: %v\n", buf)
fmt.Printf("Received size: %v\n", len(buf))
fullData := append(sizeBuf, buf...)
logEntry, err := Deserialize(fullData)
if err != nil {
return fmt.Errorf("deserialization error: %v", err)
}
formData, err := convertToFormData(logEntry)
if err != nil {
return fmt.Errorf("JSON conversion error: %v", err)
}
if err := sendToRESTAPIForm(formData); err != nil {
return fmt.Errorf("REST API error: %v", err)
}
}
}
package main
import (
"fmt"
"io/ioutil"
"sync"
"gopkg.in/yaml.v2"
)
type Config struct {
CallNo string `yaml:"call_no"`
ServerAddress string `yaml:"server_address"`
MaxRetries int `yaml:"max_retries"`
ReconnectInterval int `yaml:"reconnect_interval"`
ReadTimeout int `yaml:"read_timeout"`
RestAPIEndpoint string `yaml:"rest_api_endpoint"`
RestAPIGetDevice string `yaml:"rest_api_get_device"`
}
var (
config Config
configMutex sync.RWMutex
)
func loadConfig() error {
configMutex.Lock()
defer configMutex.Unlock()
data, err := ioutil.ReadFile("config.yaml")
if err != nil {
return err
}
if err := yaml.Unmarshal(data, &config); err != nil {
return err
}
return validateConfig(&config)
}
func validateConfig(cfg *Config) error {
if cfg.CallNo == "" || cfg.ServerAddress == "" || cfg.RestAPIEndpoint == "" {
return fmt.Errorf("missing required configuration")
}
return nil
}
func updateConfigFile(newPhoneNumber string) error {
configMutex.Lock()
defer configMutex.Unlock()
config.CallNo = newPhoneNumber
data, err := yaml.Marshal(&config)
if err != nil {
return fmt.Errorf("failed to marshal config: %v", err)
}
if err := ioutil.WriteFile("config.yaml", data, 0644); err != nil {
return fmt.Errorf("failed to write config file: %v", err)
}
return nil
}
call_no: 012-3625-9166
server_address: 118.46.137.10:20000
max_retries: 3
reconnect_interval: 5
read_timeout: 60
rest_api_endpoint: http://jda.irajas.com/setData/device_kakao_input.php
rest_api_get_device: http://jda.irajas.com/getData/json_device_info.php
package main
import (
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
)
type DeviceInfo struct {
SeqNo int `json:"seq"`
OrdNo int `json:"ordNo"`
SerialKey string `json:"serial_key"`
CallNo string `json:"call_no"`
DLCIP string `json:"dlc_ip"`
RecIP string `json:"rec_ip"`
}
func getDeviceInfo(callNo string) (*DeviceInfo, error) {
fmt.Print("Getting device info...\n")
fmt.Printf("url:%s\n", config.RestAPIGetDevice)
url := fmt.Sprintf("%s?c=%s", config.RestAPIGetDevice, callNo)
fmt.Println(url)
resp, err := http.Get(url)
if err != nil {
return nil, err
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil, err
}
var deviceInfo DeviceInfo
err = json.Unmarshal(body, &deviceInfo)
if err != nil {
return nil, err
}
return &deviceInfo, nil
}
package main
import (
"fmt"
"os/exec"
)
func resetAllGPIO() {
fmt.Println("Resetting all GPIOs...")
resetServerGPIO()
resetReciverConnectGPIO()
}
func setServerGPIO() error {
cmd := exec.Command("sh", "-c", "echo 1 > /sys/class/gpio/gpio269/value")
err := cmd.Run()
if err != nil {
fmt.Printf("GPIO 269 설정 실패: %v", err)
}
return nil
}
func resetServerGPIO() error {
cmd := exec.Command("sh", "-c", "echo 0 > /sys/class/gpio/gpio269/value")
err := cmd.Run()
if err != nil {
fmt.Printf("GPIO 269 설정 실패: %v", err)
}
return nil
}
func setReciverConnectGPIO() error {
cmd := exec.Command("sh", "-c", "echo 1 > /sys/class/gpio/gpio270/value")
err := cmd.Run()
if err != nil {
fmt.Printf("GPIO 270 설정 실패: %v", err)
}
return nil
}
func resetReciverConnectGPIO() {
cmd := exec.Command("sh", "-c", "echo 0 > /sys/class/gpio/gpio270/value")
err := cmd.Run()
if err != nil {
fmt.Printf("GPIO 270 리셋 실패: %v", err)
}
}
package main
import (
"fmt"
"net/http"
"strings"
)
func updatePhoneNumberHandler(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodPost {
http.Error(w, "Only POST method is allowed", http.StatusMethodNotAllowed)
return
}
newPhoneNumber := r.FormValue("phone_number")
if newPhoneNumber == "" {
http.Error(w, "Phone number is required", http.StatusBadRequest)
return
}
if !strings.HasPrefix(newPhoneNumber, "010-") || len(newPhoneNumber) != 13 {
http.Error(w, "Invalid phone number format", http.StatusBadRequest)
return
}
if err := updateConfigFile(newPhoneNumber); err != nil {
http.Error(w, fmt.Sprintf("Failed to update config: %v", err), http.StatusInternalServerError)
return
}
if err := loadConfig(); err != nil {
http.Error(w, fmt.Sprintf("Failed to reload config: %v", err), http.StatusInternalServerError)
return
}
w.WriteHeader(http.StatusOK)
fmt.Fprintf(w, "Phone number updated successfully to %s", newPhoneNumber)
}
package main
import (
"bytes"
"fmt"
"net/url"
"golang.org/x/text/encoding/korean"
"golang.org/x/text/transform"
)
type LogEntry struct {
Size byte
Type uint8
DateTime string
On uint8
Address [7]byte
Area [24]byte
Device [24]byte
}
func Deserialize(data []byte) (*LogEntry, error) {
fmt.Printf("Data: %v\n", data)
le := &LogEntry{}
le.Size = data[0]
if le.Size == 0 {
return nil, fmt.Errorf("invalid data length: expected , got %d", le.Size)
}
le.Type = data[1]
le.DateTime = fmt.Sprintf("%02d-%02d-%02d %02d:%02d:%02d", (int(data[2]) + 2000), data[3], data[4], data[5], data[6], data[7])
le.On = data[8]
copy(le.Address[:], data[9:16])
copy(le.Area[:], data[16:40])
copy(le.Device[:], data[40:])
return le, nil
}
func decodeEUCKR(b []byte) (string, error) {
decoder := korean.EUCKR.NewDecoder()
result, _, err := transform.Bytes(decoder, b)
if err != nil {
return "", err
}
return string(result), nil
}
func convertToFormData(le *LogEntry) (url.Values, error) {
area, err := decodeEUCKR(bytes.TrimRight(le.Area[:], "\x00"))
if err != nil {
return nil, err
}
device, err := decodeEUCKR(bytes.TrimRight(le.Device[:], "\x00"))
if err != nil {
return nil, err
}
var status string
switch le.Type {
case 1:
le.Type = 6 // 화재
case 2:
le.Type = 2 // 고장
case 3:
le.Type = 3 // 설비
case 4:
le.Type = 4 // 출력
}
status = "해제"
if le.On == 1 {
status = "동작"
}
data := url.Values{}
data.Set("callNo", config.CallNo)
data.Set("type", fmt.Sprintf("%d", le.Type))
data.Set("dateTime", le.DateTime)
data.Set("status", status)
data.Set("addr", fmt.Sprintf("%d%d-%d-%d%d%d-%d",
le.Address[0], le.Address[1], le.Address[2],
le.Address[3], le.Address[4], le.Address[5], le.Address[6]))
data.Set("zoneNm", area)
data.Set("deviceNm", device)
return data, nil
}
package main
import (
"fmt"
"log"
"os"
"path/filepath"
"sort"
"sync"
"time"
)
const (
maxLogSize = 10 * 1024 * 1024 // 10MB
maxLogFiles = 5
logFilePrefix = "lte_alarm"
)
var (
logger *log.Logger
logFile *os.File
logMutex sync.Mutex
currentLogPath string
)
func initLogger(filename string) error {
var err error
currentLogPath = filename
logFile, err = os.OpenFile(currentLogPath, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
if err != nil {
return fmt.Errorf("failed to open log file: %v", err)
}
logger = log.New(logFile, "", log.LstdFlags)
return nil
}
func closeLogger() {
if logFile != nil {
logFile.Close()
}
}
func logInfo(message string) {
logMutex.Lock()
defer logMutex.Unlock()
if logger != nil {
logger.Printf("INFO: %s", message)
checkLogSize()
}
fmt.Println(message)
}
func logError(message string) {
logMutex.Lock()
defer logMutex.Unlock()
if logger != nil {
logger.Printf("ERROR: %s", message)
checkLogSize()
}
fmt.Printf("ERROR: %s\n", message)
}
func checkLogSize() {
info, err := logFile.Stat()
if err != nil {
fmt.Printf("Error checking log file size: %v\n", err)
return
}
if info.Size() > maxLogSize {
rotateLog()
}
}
func rotateLog() {
closeLogger()
// Rename current log file
timestamp := time.Now().Format("20060102_150405")
newName := fmt.Sprintf("%s_%s.log", logFilePrefix, timestamp)
err := os.Rename(currentLogPath, newName)
if err != nil {
fmt.Printf("Error renaming log file: %v\n", err)
return
}
// Open new log file
err = initLogger(currentLogPath)
if err != nil {
fmt.Printf("Error creating new log file: %v\n", err)
return
}
// Clean up old log files
cleanupOldLogs()
}
func cleanupOldLogs() {
dir := filepath.Dir(currentLogPath)
pattern := fmt.Sprintf("%s_*.log", logFilePrefix)
matches, err := filepath.Glob(filepath.Join(dir, pattern))
if err != nil {
fmt.Printf("Error finding old log files: %v\n", err)
return
}
if len(matches) <= maxLogFiles {
return
}
sort.Slice(matches, func(i, j int) bool {
return matches[i] > matches[j] // Sort in descending order
})
for _, file := range matches[maxLogFiles:] {
err := os.Remove(file)
if err != nil {
fmt.Printf("Error removing old log file %s: %v\n", file, err)
}
}
}
package main
import (
"fmt"
"net/http"
"os"
"os/signal"
"syscall"
"time"
)
func main() {
if err := initLogger("lte_alarm.log"); err != nil {
fmt.Printf("Failed to initialize logger: %v\n", err)
os.Exit(1)
}
defer closeLogger()
logInfo("--------------------------------------")
logInfo(" LTE Alarm System : Version 2.0.0")
logInfo("--------------------------------------")
if err := loadConfig(); err != nil {
logError(fmt.Sprintf("Error loading config: %v\n", err))
os.Exit(1)
}
fmt.Println(config)
// 프로그램 종료 시 GPIO 리셋을 위한 defer 추가
defer resetAllGPIO()
// 시그널 핸들러 설정
setupSignalHandler()
// HTTP 서버 설정
http.HandleFunc("/update-phone", updatePhoneNumberHandler)
go func() {
fmt.Println("Starting HTTP server on :5119")
if err := http.ListenAndServe(":5119", nil); err != nil {
logError(fmt.Sprintf("HTTP server error: %v\n", err))
}
}()
// 장치 정보 가져오기 및 네트워크 설정
maxRetries := 5
for i := 0; i < maxRetries; i++ {
deviceInfo, err := getDeviceInfo(config.CallNo)
if err != nil {
logError(fmt.Sprintf("Error getting device info: %v\n", err))
if i == maxRetries-1 {
rebootSystem()
}
time.Sleep(1 * time.Minute)
continue
}
setServerGPIO()
err = setNetworkConfig(deviceInfo)
if err != nil {
logError(fmt.Sprintf("Error setting network config: %v\n", err))
if i == maxRetries-1 {
rebootSystem()
}
time.Sleep(1 * time.Minute)
continue
}
config.ServerAddress = deviceInfo.RecIP + ":20000"
break
}
for {
if err := runClient(); err != nil {
fmt.Printf("Error: %v\n", err)
time.Sleep(time.Duration(config.ReconnectInterval) * time.Second)
}
}
}
func setupSignalHandler() {
c := make(chan os.Signal, 1)
signal.Notify(c, os.Interrupt, syscall.SIGTERM)
go func() {
<-c
logError("\nReceived interrupt signal. Cleaning up...")
resetAllGPIO()
os.Exit(0)
}()
}
func rebootSystem() {
logInfo("Rebooting system...")
// 실제 시스템 재부팅 코드를 여기에 추가하세요
err := syscall.Reboot(syscall.LINUX_REBOOT_CMD_RESTART)
if err != nil {
logError(fmt.Sprintf("failed to reboot: %v", err))
}
os.Exit(0)
}
package main
import (
"fmt"
"os/exec"
"strings"
)
func setNetworkConfig(info *DeviceInfo) error {
fmt.Printf("Setting IP: %s\n", info.DLCIP)
return setIPAddress("eth0", info.DLCIP, "24")
}
func setIPAddress(interfaceName, ipAddress, netmask string) error {
fmt.Printf("Setting IP address: %s\n", ipAddress)
cmd := exec.Command("ip", "addr", "add", ipAddress+"/"+netmask, "dev", interfaceName)
output, err := cmd.CombinedOutput()
if err != nil {
result := string(output)
if strings.Contains(result, "File exists") {
return nil
}
return fmt.Errorf("failed to set IP address: %v, output: %s", err, output)
}
return setIfconfigUp()
}
func setIfconfigUp() error {
cmd := exec.Command("ip", "link", "set", "eth0", "up")
output, err := cmd.CombinedOutput()
if err != nil {
return fmt.Errorf("failed to set interface up: eth0, err: %v, output: %s", err, output)
}
return nil
}
package main
import (
"fmt"
"io/ioutil"
"net/http"
"net/url"
)
func sendToRESTAPIForm(data url.Values) error {
fmt.Printf("REST API Endpoint: %s\n", config.RestAPIEndpoint)
fmt.Printf("Sending data to REST API: %v\n", data)
logInfo(fmt.Sprintf("Sending data to REST API: %v\n", data))
resp, err := http.PostForm(config.RestAPIEndpoint, data)
if err != nil {
return err
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
logError(fmt.Sprintf("Error reading body: %v\n", data))
return err
}
// 결과 출력
fmt.Printf("Response Headers: %v\n", resp.Header)
fmt.Printf("Response Status: %s\n", resp.Status)
fmt.Printf("Response Body: %s\n", string(body))
return nil
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment