---
# System prepended metadata

title: Untitled

---

Arduino
(1) traffic_light.ino
```
#include <TrafficLight.h>
#include <LiquidCrystal_I2C.h>
#include <Wire.h>
#include <ArduinoJson.h>
#include <Timer.h>

// Module connection pins (Digital Pins)
#define DIO1 22
#define CLK1 23
#define DIO2 26
#define CLK2 27
#define DIO3 24
#define CLK3 25
#define DIO4 28
#define CLK4 29

//declare 4 traffic lights
TrafficLight light_a(1, 90, CLK1, DIO1); // TrafficLight(dir, deafult sec, CLK, DIO)
TrafficLight light_b(1, 90, CLK2, DIO2);
TrafficLight light_c(2, 90, CLK3, DIO3);
TrafficLight light_d(2, 90, CLK4, DIO4);

int operand;
float second;
int nowDirect; // 1->ab; 2->cd;
int lastUpdate;
const long interval = 1000;
String received;
String message;

boolean NODEMCU_DISCONNECT = 1;

Timer sendESP;

StaticJsonDocument<1024> data;
StaticJsonDocument<1024> esp_data;

LiquidCrystal_I2C lcd(0x27, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE);  // 設定 LCD I2C 位址

void setup() {
  //初始化序列埠
  initSerial();

  //初始化 lcd
  initLCD();

  // 等待 nodemcu
  waitNodeMCU();

  //等待 arduino led controller
  waitLightController();

  //設定初始秒數 (皆為 a 的初始秒數)
  //設定初始紅燈方向為 1<--->2 (1)
  // (direct, second)
  setDirAndSec(1, light_a.defaultSec + 1);

  //每 3 秒傳送秒數資料給 NodeMCU
  sendESP.every(2000, sendJSON);
}

void loop() {
  //秒數扣1
  minus();

  //判斷是否需要切換方向
  checkSwitch();

  //顯示秒數 (紅燈方向才顯示)
  showSecond();

  //把當前秒數, 當前方向 放到 data 字典檔裡面
  data["now_second"] = light_a.nowSec;
  data["now_direct"] = nowDirect;

  // 傳送資料到 NodeMCU
  sendESP.update();

  // 監聽 NodeMCU 的訊息 (秒數變動)
  listenToNodeMCU();

  //延遲一秒
  delay(1000);
}

void initSerial() {
  Serial.begin(9600); //usb slot
  Serial1.begin(9600); //for TX/RX message on arduino 2 (light controller)
  Serial2.begin(9600); // for Transmit seconds to NodeMCU(TX only)
  Serial3.begin(9600); //for Receive data from NodeMCU(RX only)
  Serial.println("hi");
}

void initLCD() {
  lcd.begin(16, 2);      // 初始化 LCD，一行 16 的字元，共 2 行，預設開啟背光
  lcd.clear();
}

void waitNodeMCU() {
  lcd.setCursor(0, 0); // 設定游標位置在第一行行首
  lcd.print("Waiting for");
  lcd.setCursor(0, 1); // 設定游標位置在第二行行首
  lcd.print("NodeMCU WIFI..");
  delay(1000);

  //busy wait..
  while (NODEMCU_DISCONNECT) {
    if (Serial3.available() > 0) {
      if (Serial3.read() == 100) {
        NODEMCU_DISCONNECT = 0;
      }
    }
  }

  //Connected!
  lcd.clear();
  lcd.setCursor(0, 0); // 設定游標位置在第一行行首
  lcd.print("WIFI Connected!");
  delay(1000);
}

void waitLightController() {
  // 等待 Light Controller
  lcd.setCursor(0, 0);
  lcd.print("Waiting for");
  lcd.setCursor(0, 1);
  lcd.print("Light Controller");
  delay(1000);

  //busy wait..
  while (Serial1.available() == 0) {}

  lcd.clear();
  lcd.setCursor(0, 0); // 設定游標位置在第一行行首
  lcd.print("Connected!");
  delay(1000);

  //lcd 顯示號誌初始狀態
  lcd.clear();
  lcd.setCursor(0, 0); // 設定游標位置在第一行行首
  lcd.print("Red: 1-2");
  lcd.setCursor(0, 1); // 設定游標位置在第一行行首
  lcd.print("Status: Normal");
}

void setDirAndSec(int direct, int sec) {
  //告知切換方向
  nowDirect = direct;
  Serial1.print(nowDirect);

  //設定秒數
  light_a.init(sec);
  light_b.init(sec);
  light_c.init(sec);
  light_d.init(sec);
}

void sendJSON() {
  serializeJsonPretty(data, Serial2);
  if (nowDirect == 1) {
    Serial1.print(nowDirect);
  } else if (nowDirect == 2) {
    Serial1.print(nowDirect);
  }

  lcd.setCursor(0, 1); // 設定游標位置在第一行行首
  lcd.print("Status: Normal  ");
}

void minus() {
  //秒數扣1
  light_a.minus(1);
  light_b.minus(1);
  light_c.minus(1);
  light_d.minus(1);
}

void checkSwitch() {
  //當秒數為 0 時, 變更方向
  if (light_a.nowSec <= 0 || light_c.nowSec <= 0) {
    lcd.setCursor(0, 1); // 設定游標位置在第一行行首

    //如果 1 <---> 2 方向是紅燈
    if (nowDirect == 1) {
      //變更方向為 3 <----> 4
      nowDirect = 2;
      Serial1.print(nowDirect);
      light_a.setSec(light_c.variaSec);
      light_b.setSec(light_c.variaSec);
      light_c.setSec(light_c.variaSec);
      light_d.setSec(light_c.variaSec);
      lcd.setCursor(0, 0); // 設定游標位置在第一行行首
      lcd.print("Red: 3-4");

    } else { //如果 3 <---> 4 方向是紅燈
      //變更方向為 1 <----> 2
      nowDirect = 1;
      Serial1.print(nowDirect);
      light_a.setSec(light_a.variaSec);
      light_b.setSec(light_a.variaSec);
      light_c.setSec(light_a.variaSec);
      light_d.setSec(light_a.variaSec);

      lcd.setCursor(0, 0); // 設定游標位置在第一行行首
      lcd.print("Red: 1-2");
    }
  }
}

void showSecond() {
  if (nowDirect == 1) {
    light_a.show();
    light_b.show();
    light_c.clear();
    light_d.clear();
  } else if (nowDirect == 2) {
    light_c.show();
    light_d.show();
    light_a.clear();
    light_b.clear();
  }
}

void listenToNodeMCU() {
  if (Serial3.available() > 0) {
    //讀取訊息
    received = Serial3.readString();

    //訊息剖析 (Deserialize)
    DeserializationError error = deserializeJson(esp_data, received);
    if (error) {
      switch (error.code()) {
        case DeserializationError::Ok:
          Serial.print(F("Deserialization succeeded"));
          break;
        case DeserializationError::InvalidInput:
          Serial.print(F("Invalid input!"));
          break;
        case DeserializationError::NoMemory:
          Serial.print(F("Not enough memory"));
          break;
        default:
          Serial.print(F("Deserialization failed"));
          break;
      }
    } else {
      //訊息萃取 (To integer)
      second = esp_data["second"];
      operand = esp_data["operand"];

      //調整秒數 (switch case)
      adjustSecond(second, operand);

      //lcd print message
      lcd.setCursor(0, 1);
      lcd.print(message);
    }
  }
}

void adjustSecond(float sec, int oper) {
  //調整秒數 1->"+"; 2->"-"; 3->"*"; 4->"/"; 5->"="
  Serial.print("adjust second:");
  Serial.println(sec);
  Serial.print("operand:");
  Serial.print(oper);
  
  switch (oper) {
    case 1: //"+"
      message = "Add ";
      light_a.add(int(sec));
      light_b.add(int(sec));
      light_c.add(int(sec));
      light_d.add(int(sec));
      break;
    case 2: //"-"
      message = "Minus ";
      if ((light_c.nowSec - sec ) <= 0) {
        light_a.nowSec = 0;
        light_b.nowSec = 0;
        light_c.nowSec = 0;
        light_d.nowSec = 0;
      } else {
        light_a.minus(int(sec));
        light_b.minus(int(sec));
        light_c.minus(int(sec));
        light_d.minus(int(sec));
      }
      break;
    case 3: //"*"
      message = "Mutiply ";
      light_a.multiply(sec);
      light_b.multiply(sec);
      light_c.multiply(sec);
      light_d.multiply(sec);
      break;
    case 4: //"/"
      message = "Divide ";
      light_a.divide(sec);
      light_b.divide(sec);
      light_c.divide(sec);
      light_d.divide(sec);
      break;
    case 5: //"="
      message = "Divide ";
      light_a.setSec(int(sec));
      light_b.setSec(int(sec));
      light_c.setSec(int(sec));
      light_d.setSec(int(sec));
      break;
  }
}
```

(2)control_led
```
#define RED1 22
#define GREEN1 23
#define RED2 26
#define GREEN2 27
#define RED3 24
#define GREEN3 25
#define RED4 28
#define GREEN4 29
 
String DIR1 = "49"; //ASCII -> 1
String DIR2 = "50"; //ASCII -> 2

String received;

void setup() {
  initSerial();

  initLight();

  Serial1.write(100); //transmit init message to main controller
}

void loop() {
  //listen to main controller
  listenToMainController();
}

void initSerial() {
  Serial.begin(9600);
  Serial1.begin(9600);
}

void initLight() {
  pinMode(RED1, OUTPUT);
  pinMode(GREEN1, OUTPUT);
  pinMode(RED2, OUTPUT);
  pinMode(GREEN2, OUTPUT);
  pinMode(RED3, OUTPUT);
  pinMode(GREEN3, OUTPUT);
  pinMode(RED4, OUTPUT);
  pinMode(GREEN4, OUTPUT);

  digitalWrite(RED1, HIGH);
  digitalWrite(GREEN1, LOW);
  digitalWrite(RED2, HIGH);
  digitalWrite(GREEN2, LOW);
  digitalWrite(RED3, LOW);
  digitalWrite(GREEN3, HIGH);
  digitalWrite(RED4, LOW);
  digitalWrite(GREEN4, HIGH);
}

void listenToMainController() {
  if (Serial1.available() > 0) {
    received = Serial1.read();
    Serial.println(received);

    if (received == DIR1) {
      digitalWrite(RED1, HIGH);
      digitalWrite(GREEN1, LOW);
      digitalWrite(RED2, HIGH);
      digitalWrite(GREEN2, LOW);
      digitalWrite(RED3, LOW);
      digitalWrite(GREEN3, HIGH);
      digitalWrite(RED4, LOW);
      digitalWrite(GREEN4, HIGH);

    } else if (received == DIR2) {
      digitalWrite(RED1, LOW);
      digitalWrite(GREEN1, HIGH);
      digitalWrite(RED2, LOW);
      digitalWrite(GREEN2, HIGH);
      digitalWrite(RED3, HIGH);
      digitalWrite(GREEN3, LOW);
      digitalWrite(RED4, HIGH);
      digitalWrite(GREEN4, LOW);
    }
  }
}
```

NodeMCU
```
#include <ArduinoJson.h>

#include <ESP8266WiFi.h>
#include <ESP8266HTTPClient.h>
#include <ESP8266WebServer.h>


const char* ssid = "ASUS_Aaron_2G";
const char* password = "0981429828";

WiFiServer wifiServer(2345);
ESP8266WebServer server(80);

HTTPClient http;

StaticJsonDocument<200> doc;

StaticJsonDocument<200> doc1;
JsonObject data = doc1.to<JsonObject>();

String received = "";
String url = "";
String page, page2, page3;
int now;
int past;
int httpCode;
int value;
int sending;

String second, operand;
long now_direct, now_second;

void setup() {
  initSerial();

  //waiting for wifi connection
  initWIFI();

  //start wifi server
  initWIFIServer();

  //send init message to main controller
  Serial1.write(100);
}

void loop() {
  listenToMainController();
  server.handleClient();
}

void listenToMainController() {
  while (Serial.available() > 0) {
    received = Serial.readString();
    Serial.println(received);
    DeserializationError error = deserializeJson(doc, received);
    JsonObject data = doc.as<JsonObject>();
    now_second = data["now_second"];
    now_direct = data["now_direct"];

    passInfo(now_direct, now_second);
  }
}

void initSerial() {
  Serial.begin(9600); // for receiving seconds from Arduino
  Serial1.begin(9600);// for transmitting operator and operands //D4
}

void initWIFI() {
  WiFi.begin(ssid, password);

  while (WiFi.status() != WL_CONNECTED) {
    Serial.println("Connecting...");
    delay(1000);
  }

  Serial.print("Conntected to Wifi. IP:");
  Serial.println(WiFi.localIP());

  wifiServer.begin();
  Serial.println("port 2345 opened");
  Serial.println(WiFi.macAddress());
}

void initWIFIServer() {
  //page info
  page = "<h1>NodeMCU Web Server</h1>";

  server.on("/", []() {
    server.send(200, "text/html", page);
  });

  server.on("/change", []() {
    operand = server.arg(0);
    second = server.arg(1);
    page2 = "<h1>Operand:" + operand;
    page2 += "</h1><h1><p>Data:";
    page2 += second;
    page2 += "</p></h1>";
    server.send(200, "text/html", page2);

    data["operand"] = operand;
    data["second"] = second;
    Serial.print("operand:");
    Serial.print(operand);
    Serial.println("second:");
    Serial.print(second);

    //send message to Arduino
    serializeJsonPretty(data, Serial1);
  });

  server.begin();
}

void passInfo(int dir, int sec) {
  url = "http://192.168.50.46/arduino/" + String(dir) + "/" + String(sec);
  http.begin(url);
  httpCode = http.GET();
  http.end();
}
```

Custom Library
(1)TrafficLight.h
```
#define TrafficLight_h
#include "TM1637Display.h"

class TrafficLight
{
public:
	TrafficLight(int dir, int sec, int CLK, int DIO);
	void init(int sec);
	void show();
	void add(int sec);
	void minus(int sec);
	void multiply(float sec);
	void divide(float sec);
	void setSec(int sec);
	void changeVariaSec(int sec);
	void clear();
	int direction;
	int defaultSec;
	int nowSec;
	int variaSec;
	TM1637Display TM1637;
};

```

(2)TrafficLight.cpp
```
#include "TrafficLight.h"

TrafficLight::TrafficLight(int dir, int sec, int CLK, int DIO):TM1637(CLK, DIO)
{
	direction = dir;
	defaultSec = sec;
	variaSec = sec;
}

void TrafficLight::init(int sec){
	TM1637.setBrightness(0x0f);
	nowSec = sec;
	TM1637.showNumberDec(sec);
}

void TrafficLight::show(){
	TM1637.showNumberDec(nowSec);
}

void TrafficLight::setSec(int sec){
	nowSec = sec;
}

void TrafficLight::add(int sec){
	nowSec += sec;
}

void TrafficLight::minus(int sec){
	nowSec -= sec;
}

void TrafficLight::multiply(float sec){
	float temp = nowSec * sec;
	nowSec = int(temp);
}

void TrafficLight::divide(float sec){
	float temp = nowSec / sec;
	nowSec = int(temp);
}

void TrafficLight::changeVariaSec(int sec){
	variaSec -= sec;
}

void TrafficLight::clear(){
	TM1637.clear();
}

```


Raspberry Pi Camera Server
(1)app.py
```
#!/usr/bin/env python
from importlib import import_module
import os
from flask import Flask, render_template, Response, request

from camera_opencv import Camera

app = Flask(__name__)

def gen2(camera):
    """Returns a single image frame"""
    frame = camera.get_frame()
    yield frame

@app.route('/image')
def image():
    """Returns a single current image for the webcam"""
    cameraID = request.args.get('cid')
    return Response(gen2(Camera()), mimetype='image/jpeg')

def gen(camera):
    while True:
        frame = camera.get_frame()
        yield (b'--frame\r\n'
               b'Content-Type: image/jpeg\r\n\r\n' + frame + b'\r\n\r\n')

@app.route('/stream')
def stream():
    cameraID = request.args.get('cid')
    #return Response(gen(VideoCamera(cameraID)), mimetype='multipart/x-mixed-replace; boundary=frame')
    return Response(gen(Camera()), mimetype='multipart/x-mixed-replace; boundary=frame')

if __name__ == '__main__':
    app.run(host='0.0.0.0', port='5000', threaded=True)
```

(2)base_camera.py
```
import time
import threading
try:
    from greenlet import getcurrent as get_ident
except ImportError:
    try:
        from thread import get_ident
    except ImportError:
        from _thread import get_ident


class CameraEvent(object):
    """An Event-like class that signals all active clients when a new frame is
    available.
    """
    def __init__(self):
        self.events = {}

    def wait(self):
        """Invoked from each client's thread to wait for the next frame."""
        ident = get_ident()
        if ident not in self.events:
            # this is a new client
            # add an entry for it in the self.events dict
            # each entry has two elements, a threading.Event() and a timestamp
            self.events[ident] = [threading.Event(), time.time()]
        return self.events[ident][0].wait()

    def set(self):
        """Invoked by the camera thread when a new frame is available."""
        now = time.time()
        remove = None
        for ident, event in self.events.items():
            if not event[0].isSet():
                # if this client's event is not set, then set it
                # also update the last set timestamp to now
                event[0].set()
                event[1] = now
            else:
                # if the client's event is already set, it means the client
                # did not process a previous frame
                # if the event stays set for more than 5 seconds, then assume
                # the client is gone and remove it
                if now - event[1] > 5:
                    remove = ident
        if remove:
            del self.events[remove]

    def clear(self):
        """Invoked from each client's thread after a frame was processed."""
        self.events[get_ident()][0].clear()


class BaseCamera(object):
    thread = None  # background thread that reads frames from camera
    frame = None  # current frame is stored here by background thread
    last_access = 0  # time of last client access to the camera
    event = CameraEvent()

    def __init__(self):
        """Start the background camera thread if it isn't running yet."""
        if BaseCamera.thread is None:
            BaseCamera.last_access = time.time()

            # start background frame thread
            BaseCamera.thread = threading.Thread(target=self._thread)
            BaseCamera.thread.start()

            # wait until frames are available
            while self.get_frame() is None:
                time.sleep(0)

    def get_frame(self):
        """Return the current camera frame."""
        BaseCamera.last_access = time.time()

        # wait for a signal from the camera thread
        BaseCamera.event.wait()
        BaseCamera.event.clear()

        return BaseCamera.frame

    @staticmethod
    def frames():
        """"Generator that returns frames from the camera."""
        raise RuntimeError('Must be implemented by subclasses.')

    @classmethod
    def _thread(cls):
        """Camera background thread."""
        print('Starting camera thread.')
        frames_iterator = cls.frames()
        
        for frame in frames_iterator:
            BaseCamera.frame = frame
            BaseCamera.event.set()  # send signal to clients
            time.sleep(0)

            # if there hasn't been any clients asking for frames in
            # the last 10 seconds then stop the thread
            if time.time() - BaseCamera.last_access > 10:
                try:
                    frames_iterator.close()
                    print('Stopping camera thread due to inactivity.')
                    
                except GeneratorExit:
                    print("Generator Exit error")
                finally:
                    print("finally")
                    break
        BaseCamera.thread = None

```

(3)camera_opencv.py
```
import cv2
from base_camera import BaseCamera


class Camera(BaseCamera):
    video_source = 0
    @staticmethod
    def set_video_source(source):
        Camera.video_source = source

    @staticmethod
    def frames():
        camera = cv2.VideoCapture(Camera.video_source)
        camera.set(cv2.CAP_PROP_FRAME_WIDTH, 1280)
        camera.set(cv2.CAP_PROP_FRAME_HEIGHT, 720)
        if not camera.isOpened():
            raise RuntimeError('Could not start camera.')

        while True:
            # read current frame
            _, img = camera.read()

            # encode as a jpeg image and return it
            try:
                yield cv2.imencode('.jpg', img)[1].tobytes()
            except GeneratorExit:
                print("image encode error")
```



Python Task Server
```
# -*- coding: utf-8 -*-
"""
Created on Tue Jan 29 13:29:40 2019

@author: chaos
"""

from darkflow.net.build import TFNet
import cv2

from io import BytesIO
import time
import requests
from PIL import Image, ImageDraw, ImageFont
import numpy as np
from time import gmtime, strftime
import threading
import queue
import pygame 
import boto3
import json
import webbrowser

#darkflow
options = {"model": "cfg/yolo.cfg", "load": "bin/yolov2.weights", "threshold": 0.05}
tfnet = TFNet(options) 

#line notify
_token = 'yourToken' #管理單位
token = 'yourAnotherToken' #維修單位

#laravel API
maintenance_url = 'http://127.0.0.1/api/maintenance/generate'

#sns
arnID='yourARNID'
count_dict = {"cam1":0, "cam2":0, "cam3":0, "cam4":0}
error_count_dict = {"cam1":0, "cam2":0, "cam3":0, "cam4":0}

#功能開關(default=False)
OPEN_NOTIFY = False
OPEN_RULE = False

#排程時間
timegap = 2

#記錄 物件辨識最後一次的執行時間
lastUpdated = int(round(time.time()))

#pygame 基本設定
pygame.init() 
pygame.font.init()
screen = pygame.display.set_mode((1280, 870))
image = pygame.image.load('cars/error.jpg')
icon = pygame.image.load('images/icon.png')
waiting = pygame.image.load("images/waiting.png")
waiting = pygame.transform.scale(waiting, (400, 400))
warning = pygame.image.load("images/warning.png")
pygame.display.set_caption('監控台')
pygame.display.set_icon(icon)

#pygame 各鏡頭圖片位置
camPic_pos = {
    1: [0, 150],
    2: [640, 150],
    3: [0, 510],
    4: [640, 510]
}

#pygame 各鏡頭文字位置
camTitle_pos = {
    1: [1, 160],
    2: [650, 160],
    3: [10, 520],
    4: [650, 520]
}

cam_title = {
    1: pygame.image.load('images/cam1_title.png'),
    2: pygame.image.load('images/cam2_title.png'),
    3: pygame.image.load('images/cam3_title.png'),
    4: pygame.image.load('images/cam4_title.png')
}

#初始畫面
icon_color = pygame.image.load('images/icon_color.png')
icon = pygame.transform.scale(icon_color, (300, 300))
screen.blit(icon, (160, 230))

#pygame 文字位置
font_title = pygame.font.Font('fonts/NotoSansCJKtc-Regular.otf', 40)
font_init_message = pygame.font.Font('fonts/NotoSansCJKtc-Regular.otf',80)

#起始訊息
init_message = font_init_message.render("監控系統啟動中", True, (255, 255, 255))
screen.blit(init_message, (450, 350))

#地址
district = font_title.render("新北市 新莊區", True, (255, 255, 0))
street = font_title.render("中正路/建國一路 交叉口", True, (0, 255, 0))
screen.blit(district, (10, 10))
screen.blit(street, (10, 70))

#模式
rule_status_title = font_title.render("號誌控制模式: ", True, (255, 255, 255))
rule_status = font_title.render("手動", True, (255, 0, 0))
screen.blit(rule_status_title, (840, 10))
screen.blit(rule_status, (1100, 10))

#推播
notify_status_title = font_title.render("推播功能: ", True, (255, 255, 255))
notify_status = font_title.render("關閉", True, (255, 0, 0))
screen.blit(notify_status_title, (920, 70))
screen.blit(notify_status, (1100, 70))

#更新畫面
pygame.display.update()

#PIL draw on picture
pilFont = ImageFont.truetype("fonts/NotoSansCJKtc-Regular.otf", 40)

def pygame_event():
    global OPEN_NOTIFY
    global OPEN_RULE

    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            pygame.quit()
            exit()
        elif event.type == pygame.MOUSEBUTTONUP:
            pos = pygame.mouse.get_pos()
            
            #符合點擊區域
            if (pos[0] > 1100 and pos[0] < 1200):
                #規則
                if (pos[1] > 0 and pos[1] < 60):
                    screen.fill(pygame.Color("black"), (1100, 0, 1500, 65))
                    if (OPEN_RULE == True):
                        OPEN_RULE = False
                        rule_status = font_title.render("手動", True, (255, 0, 0))
                        screen.blit(rule_status, (1100, 10))
                        print("close rule")
                    else:
                        OPEN_RULE = True
                        rule_status = font_title.render("自動", True, (255, 255, 0))
                        screen.blit(rule_status, (1100, 10))
                        print("open rule")
                    return True
                #通知
                elif (pos[1] > 60 and pos[1] < 120):
                    screen.fill(pygame.Color("black"), (1100, 70, 1500, 120))
                    if (OPEN_NOTIFY == True):
                        OPEN_NOTIFY = False
                        notify_status = font_title.render("關閉", True, (255, 0, 0))
                        screen.blit(notify_status, (1100, 70))
                        print("close notify")
                    else:
                        OPEN_NOTIFY = True
                        notify_status = font_title.render("開啟", True, (255, 255, 0))
                        screen.blit(notify_status, (1100, 70))
                        print("open notify")
                    return True
    return False

def sns(message):
    print("發sns")
    print(message)
    print(type(message))
    sns = boto3.client(
    'sns',
    aws_access_key_id="yourAccessKey",
    aws_secret_access_key="yourSecretAccessKey",
    aws_session_token='yourToken',
    region_name='yourRegion',
    )

    sns.publish(
        TopicArn='yourARNTopic',
        Message = message
    )

    return None

def lineNotify(token, msg, camID):
    headers = {
            "Authorization": "Bearer " + token, 
            }
    payload = {'message': msg}

    if (camID == 0):
        try:
            session = requests.Session()
            r = session.post("https://notify-api.line.me/api/notify", headers = headers, params = payload, timeout=3)
            print(r.status_code)
            print("success(pure message)")
            return None
        except:
            print("error(pure message)")
            return None
    
    url = "C:/Users/Darkflow/Desktop/darkflow/cars/cam"+str(camID)+".jpg"
    imageFile = {'imageFile': open(url, 'rb')}
    
    try:
        session = requests.Session()
        r = session.post("https://notify-api.line.me/api/notify", headers = headers, params = payload, files=imageFile, timeout=3)
        return r.content
    except:
        return None

def isHardwareAlive():
    try:
        get_second = requests.get("http://192.168.50.126/", timeout=3)
        print("hardware health")
        return True
    except:
        print('Hardware Failed')
        return False

def hardwareHealthCheck():
    #硬體無回應
    if not isHardwareAlive():
        if (OPEN_NOTIFY):
            #laravel 產生維修單
            try:
                headers = {'authorization': "Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJodHRwOlwvXC8xMjcuMC4wLjFcL2FwaVwvYXV0aFwvbG9naW4iLCJpYXQiOjE1NTgzNDk5MTcsImV4cCI6MTU2Njk4OTkxNywibmJmIjoxNTU4MzQ5OTE3LCJqdGkiOiIxUlNTUlEwU0xDTFhHbEVGIiwic3ViIjoxLCJwcnYiOiI4N2UwYWYxZWY5ZmQxNTgxMmZkZWM5NzE1M2ExNGUwYjA0NzU0NmFhIn0.Q42vS5ZghcT4uRAFccoQIZfBWHBBx8vLJ-BjYkuQDQY"}
                payload = {'intersections_id': 1, 'content': '[A01]硬體控制器異常'}
                maintenance = requests.post(maintenance_url, headers = headers, params=payload)
                response_data = json.loads(maintenance.text)

                #代表維修單正常生成，未重複
                if (response_data["notify"] == 1):
                    #發sns
                    try:
                        sns("[A01]硬體控制器異常")
                    except:
                        print("sns error(hardware)")

                    #發line notify
                    message = '錯誤事件:[A01]硬體控制器無回應'
                    print(lineNotify(token, message, 0))
                
            except:
                print('Laravel API Timeout(hardware maintenance)')
                return None


def getCarCount(url, cam_id):
    try:
        r = requests.get(url, timeout=3)
    except:
        #計算錯誤次數
        error_count_dict["cam"+str(cam_id)] += 1
        #鏡頭無回應
        print("camID:", cam_id, " timeout.")

        #錯誤次數達 3 次
        if (error_count_dict["cam"+str(cam_id)] >= 4):
            #顯示"warning"圖片
            screen.blit(warning, camPic_pos[cam_id])
            screen.blit(cam_title[cam_id], camTitle_pos[cam_id]) #顯示標題
            pygame.display.flip()

            #推播
            if (OPEN_NOTIFY):
                #laravel 產生維修單
                try:
                    headers = {'authorization': "Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJodHRwOlwvXC8xMjcuMC4wLjFcL2FwaVwvYXV0aFwvbG9naW4iLCJpYXQiOjE1NTgzNDk5MTcsImV4cCI6MTU2Njk4OTkxNywibmJmIjoxNTU4MzQ5OTE3LCJqdGkiOiIxUlNTUlEwU0xDTFhHbEVGIiwic3ViIjoxLCJwcnYiOiI4N2UwYWYxZWY5ZmQxNTgxMmZkZWM5NzE1M2ExNGUwYjA0NzU0NmFhIn0.Q42vS5ZghcT4uRAFccoQIZfBWHBBx8vLJ-BjYkuQDQY"}
                    payload = {'intersections_id': 1, 'content': '[C01]鏡頭1無回應'}
                    url = "C:/Users/Darkflow/Desktop/darkflow/cars/cam"+str(cam_id)+".jpg"
                    imageFile = {'imageFile': open(url, 'rb')}
                    maintenance = requests.post(maintenance_url, headers = headers, params=payload, files = imageFile)
                    response_data = json.loads(maintenance.text)

                    #代表維修單正常生成，未重複
                    if (response_data["notify"] == 1):
                        #發sns
                        try:
                            sns("[C01]鏡頭1無回應")
                        except:
                            print("sns error")

                        #發line notify
                        message = '[C01]鏡頭1無回應'
                        print(lineNotify(token, message, cam_id))
                except:
                    print('Laravel API Timeout(maintenance)')

        #錯誤次數未達3次
        elif (error_count_dict["cam"+str(cam_id)] == 2):
            #顯示"waiting"圖片
            screen.blit(waiting, camPic_pos[cam_id])
            screen.blit(cam_title[cam_id], camTitle_pos[cam_id]) #顯示標題
            pygame.display.flip()
        
        return 0

    #成功取得影像,刷新錯誤次數
    error_count_dict["cam"+str(cam_id)] = 0

    #影像處理
    curr_img = Image.open(BytesIO(r.content))
    curr_img_cv2 = cv2.cvtColor(np.array(curr_img), cv2.COLOR_RGB2BGR)

    #辨識物體
    result = tfnet.return_predict(curr_img_cv2)

    #影像轉 PIL ImageDraw Object
    draw = ImageDraw.Draw(curr_img)
    
    #圈出物體位置
    cars = drawCar(draw, result)

    #保存畫完的圖片到 local
    curr_img.save('cars/cam%d.jpg' %(cam_id))

    #在pygame顯示圖片
    
    image = pygame.image.load('cars/cam{}.jpg'.format(cam_id)) #讀取上一行保存的圖片
    picture = pygame.transform.scale(image, (640, 360)) #調整圖片尺寸
    screen.blit(picture, camPic_pos[cam_id]) #放到screen
    screen.blit(cam_title[cam_id], camTitle_pos[cam_id]) #顯示標題
    pygame.display.update() #更新畫面
    
    message = "鏡頭"+str(cam_id)+"偵測到車輛:"+str(cars)+"台"
    print("camera %d: %d cars detected"%(cam_id, cars))

    #存入車輛數
    count_dict["cam"+str(cam_id)] = cars

    return None

def drawCar(image, result):
    carsSeen = 0

    for detection in result:
        if detection['label'] == 'car' :
            #curr_img.save('cars/%i.jpg' %calculator)
            x_range = detection['bottomright']['x'] - detection['topleft']['x']
            if (x_range < 300):
                carsSeen += 1

                if detection['label'] == 'car':
                    image.rectangle([detection['topleft']['x'], detection['topleft']['y'], 
                            detection['bottomright']['x'], detection['bottomright']['y']],
                            outline=(99, 54, 254), width=5)
                    
                    image.rectangle([detection['topleft']['x'], detection['topleft']['y'] - 42, 
                            detection['topleft']['x'] + 80, detection['topleft']['y']],
                            fill=(99, 54, 254))
                else: #motorbike
                    image.rectangle([detection['topleft']['x'], detection['topleft']['y'], 
                            detection['bottomright']['x'], detection['bottomright']['y']],
                            outline=(255, 255, 0), width=5)
                    
                    image.rectangle([detection['topleft']['x'], detection['topleft']['y'] - 42, 
                            detection['topleft']['x'] + 200, detection['topleft']['y']],
                            fill=(255, 255, 0))
                image.text((detection['topleft']['x'] + 8, detection['topleft']['y'] - 48), detection['label'], font=pilFont, fill=(0,0,0,255))

    return carsSeen

def detectAndCount():
    que = queue.Queue();
    thread_list = list();
    
    cam_1 = threading.Thread(target=lambda q, arg1, arg2: q.put(getCarCount(arg1, arg2)), args=(que, 'http://192.168.50.172:5000/image', 1))
    cam_2 = threading.Thread(target=lambda q, arg1, arg2: q.put(getCarCount(arg1, arg2)), args=(que, 'http://192.168.50.172:5001/image', 2))
    cam_3 = threading.Thread(target=lambda q, arg1, arg2: q.put(getCarCount(arg1, arg2)), args=(que, 'http://192.168.50.206:5002/image', 3))
    cam_4 = threading.Thread(target=lambda q, arg1, arg2: q.put(getCarCount(arg1, arg2)), args=(que, 'http://192.168.50.206:5003/image', 4))
    cam_1.start()
    thread_list.append(cam_1);
    cam_2.start()
    thread_list.append(cam_2);
    cam_3.start()
    thread_list.append(cam_3);
    cam_4.start()
    thread_list.append(cam_4);

    for t in thread_list:
        t.join()

    while que.qsize() != 4:
        continue

    que.get()
    que.get()
    que.get()
    que.get()

    print("cam1:", count_dict["cam1"])
    print("cam2:", count_dict["cam2"])
    print("cam3:", count_dict["cam3"])
    print("cam4:", count_dict["cam4"])
    return None

def judgeRule(isOPENRULE, l1_car, l2_car, l3_car, l4_car):
    if isOPENRULE:
            judgeURL = "http://127.0.0.1/judgeRule/1/{light1}/{light2}/{light3}/{light4}/1".format(light1=l1_car, light2=l2_car, light3=l3_car, light4=l4_car)
            # lineNotify(_token, '中正路/建國一路交叉口 已套用規則', 0)
    else:
        judgeURL = "http://127.0.0.1/judgeRule/1/{light1}/{light2}/{light3}/{light4}/0".format(light1=l1_car, light2=l2_car, light3=l3_car, light4=l4_car)
    
    try:
        currentRule = requests.get(judgeURL, timeout=2)
        result = "Rule Judge: {id}".format(id=currentRule.content)
        print(result)
        return result
    except:
        err = "Laravel API Timeout(judge rule)"
        print(err)
        return err

def main():
    global lastUpdated
    global timegap

    while True:
        #如果pygame發生事件
        if pygame_event():
            #更新畫面
            pygame.display.flip()

        #執行排程任務
        if (int(round(time.time())) - lastUpdated > timegap):
            #辨識車輛數
            detectAndCount()
            
            #規則比較  
            judge = threading.Thread(target = judgeRule, args=[OPEN_RULE, count_dict["cam1"], count_dict["cam2"], count_dict["cam3"], count_dict["cam4"]])
            judge.start()
            #重置計時器
            lastUpdated = int(round(time.time()))

            #硬體健康檢測
            hardwareHealthCheck()

if __name__ == '__main__':
    main()


```